Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React + Redux + SSR: Cannot find store only when navigating directly to connected component

I'm relatively new to SSR with React and Redux, so I assume I'm making a simple mistake that reflects my lack of understanding of the underlying frameworks. However, I'm about to put my head through a wall, so here goes. Hope someone can help.

If I try to navigate directly to the root page (/), I get the error Could not find "store" in the context of "Connect(Home)". In my case, App contains my shared navigation logic and global content, and (/) renders a Home component. If I navigate to a page that does NOT connect to Redux (e.g. Terms of Service), and THEN navigate to my home page, it renders perfectly. I assume this is some kind of server-side routing issue, but I've read everything I can find online, tried what feels like every possible permutation of state / store / context handling, and I can't resolve it.

Here is the skeleton of my code.

renderer.js

[...]

const modules = [];
const routerContext = {};

const bundle = (
    <Loadable.Capture report={m => modules.push(m)}>
        <ReduxProvider store={store}>
            <StaticRouter location={req.baseUrl} context={routerContext}>
                <CookiesProvider cookies={req.universalCookies}>
                    <App />
                </CookiesProvider>
            </StaticRouter>
        </ReduxProvider>
    </Loadable.Capture>
    );

const html = ReactDOMServer.renderToString(bundle);

if (routerContext.url) {
  redirect(301, routerContext.url);
}

// inject into HTML

[...]

configureStore.js

[...]

const createStoreWithMiddleware = compose(applyMiddleware(ReduxThunk))(createStore);

export default function configureStore(initialState = {}) {
    return createStoreWithMiddleware(rootReducer, initialState);
};

index.js (client)


const store = configureStore( window.__REDUX_STATE__ || {} );

const AppBundle = (
    <ReduxProvider store={store}>
        <BrowserRouter>
            <CookiesProvider>
                <App />
            </CookiesProvider>
        </BrowserRouter>
    </ReduxProvider>
);

window.onload = () => {
    Loadable.preloadReady().then(() => {
        ReactDOM.hydrate(
            AppBundle,
            document.getElementById('root')
        );
    });
};

app.js ("/" renders the Home component)

[...]

export default withCookies(App);

[...]

home.js

[...]

export default connect(mapStateToProps)(Home);

[...]
like image 671
Rahul Jaswa Avatar asked Dec 01 '25 05:12

Rahul Jaswa


2 Answers

I finally identified the problem. I was running an Express server to host server-rendered pages concurrently with the Node client. Unfortunately, the server was creating one instance of React, while the client was creating another instance of React. As a result, even though the code looked right, my Redux store wasn't being passed down the component hierarchy for the same React context.

Took me forever to debug and identify. This article finally pointed me in the right direction ... but it didn't get me all the way since the React debugger won't work on a server-rendered application.

There are multiple possible solutions to the problem. In my case, the simplest resolution was to consolidate all my dependencies across the client and the server, which implicitly removed all the redundant React instances.

Really hope this answer helps someone else because I spent more than a month trying to debug it once or twice a day a week.

like image 68
Rahul Jaswa Avatar answered Dec 06 '25 02:12

Rahul Jaswa


The store can be accessed from child components if you expose it through context or through props.
You could also expose it by adding the store to the window object, but that would not be a good practice because every visitor in your site can see and change the Store. (it can be useful if you don't care about safety of the redux store).
You could also use a setter, as suggested on this answer: Redux state value globally

The redux provider makes the store available to all of the children through context.

That Provider tag renders your parent component, lets call it the App component which in turn renders every other component inside the application.

Here is the key part, when we wrap a component with the connect() function, that connect() function expects to see some parent component within the hierarchy that has the Provider tag.

Invariant Violation: Could not find "store" in either the context or props of "Connect(SportsDatabase)"

So, according to the answer linked, your Home component must have a Provider or the store itself in it's context, that may be the cause of your error.

like image 25
Eliâ Melfior Avatar answered Dec 06 '25 03:12

Eliâ Melfior



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!