Search code examples
reactjsnext.jsreact-context

NextJS using React's Context API with a layout component wrapper


My setup is as following. I have a layout component where I add a navigation component and children. I wrap them with my Provider like this

AppLayout.js

<Layout>
  <Navigation/>
  <main>
    {children}
  </main>
</Layout>

Now the <Navigation/> component has access to the data returned by React.useContext(ContextProvider). However a child component of Layout does not! If I try to console.log(state) in the <Navigation/> component I get back the actual data as expected.

But when I have a child component like this:

Child.js

<AppLayout>
 <div>Some content</div>
</AppLayout>

The state is always undefined. The only way I managed to solve this issue was by wrapping the Provider around the Component in the _app.js file. Does anyone know how to solve this without wrapping the provider around the <Component/> from _app.js file?


Solution

  • That is not possible.
    Every component that should have access to one context MUST be descendants of THE SAME context provider.

    If you put the context provider inside a wrapper component, and this wrapper component is used in multiple different positions, each instance has a separate context provider, which is a different state. That just does not work.

    If you do not want your context provider to be in _app.js, then you may put it inside any child component, but still every component that wants to use the state has to be a descendant of the context provider.

    e.g.:

    <App>
        <NoDoesNotHaveStore />
        <ContextProvider store={ myStore }>
            <YesDoesHaveStore />
            <YesAlsoDoesHaveStore />
        </ContextProvider>
    </App>
    

    You may wrap the context provider:

    const SomeWrapper = function(props){
        return <ContextProvider store={ myStore }>
           { props.children }
        </ContextProvider>;
    };
    
    <App>
        <NoDoesNotHaveStore />
        <SomeWrapper>
            <YesDoesHaveStore />
            <YesAlsoDoesHaveStore />
        </SomeWrapper>
        <NoStoreAgain />
    </App>
    

    But not multiple times:

    const SomeWrapper = function(props){
        return <ContextProvider store={ myStore }>
           { props.children }
        </ContextProvider>;
    };
    
    <App>
        <SomeWrapper>
            <HasStoreA />
        </SomeWrapper>
        <SomeWrapper>
            <HasDifferentStoreB />
        </SomeWrapper>
    </App>
    

    Also note that in Next.js, under certain circumstances, the store might be different on server side vs. client side, or on first render vs. clicking a Next-Link.