简体   繁体   中英

Next.js context provider wrapping App component with page specific layout component giving undefined data

I have an auth context component where I'm wrapping my main app component, but at the same time I'm also trying to do page specific layout component per Next.js documentation here:https://nextjs.org/docs/basic-features/layouts#per-page-layouts

Am I doing this correctly, because I can't seem to be getting the data from my Context provider.

/context/AuthContext.js

const UserContext = createContext({});

export default function AuthContext({children}) {
  // .. code
  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
}

export const useUser = () => useContext(UserContext);

/_app.js

function MyApp({ Component, pageProps }) {    

  const getLayout = Component.getLayout || ((page) => page);

  return getLayout(
    <div>
      <AuthContext>
        <Component {...pageProps} />
      </AuthContext>
    </div>
  );
}

export default MyApp;

/components/Project/List.js

import { useUser } from "../../context/AuthContext";

const ProjectList = () => {
  const { user } = useUser();
  console.log("get user data", user);

  return (
    <>
      test
    </>
  );
};

export default ProjectList;

I'm trying to console log the user, but it's giving me undefined. I'm thinking it's because the way it's wrapped as a layout component? I could be doing this wrong. But I did console log inside my AuthContext for user, and the information there is correct.

/pages/projects/index.js

const Projects = () => {
  // code goes here
  return (
    <div>
      code goes here
    </div> 
  )
}

export default Projects;

Projects.getLayout = function getLayout(page) {
  return <ProjectLayout>{page}</ProjectLayout>;
};

When I remove the Projects.getLayout block of code, the data comes back, but when I add this code, data is gone.

/components/Project/Layout.js

const ProjectLayout = ({children}) => {
  return (
    <>
      <ProjectList />
      {children}
    </>
  }

export default ProjectLayout

With your current structure ProjectLayout isn't getting wrapped by the AuthContext , meaning you won't have access to its context.

You can modify your _app 's structure and move the getLayout call around so that the context wraps it properly.

function MyApp({ Component, pageProps }) {    
    const getLayout = Component.getLayout || ((page) => page);

    return (
        <AuthContext>
            {getLayout(<Component {...pageProps} />)}
        </AuthContext>
    );
}

Note that calling getLayout inside the Context could lead to errors if the getLayout function uses hooks or stuff that depends on a parent element .

It will be calling getLayout first and then the context, so the value of it will be initially the default (fallback) value (it's like doing foo(bar()) and expecting that foo will be called before bar ).

To avoid this, return directly the component (using getLayout as a function that generates a component):

// /pages/projects/index.js
Projects.getLayout = (children) => (
  // can't use hooks here, return the component immediately
  <ProjectLayout>{children}</ProjectLayout>;
);

or use the layout as a component:

// /pages/projects/index.js
Projects.Layout = ({ children }) => {
  return <ProjectLayout>{children}</ProjectLayout>;
};

// /pages/_app.js
export default function App({ Component, pageProps }) {    
  const Layout = Component.Layout || (({ children }) => children);
  return (
     <AuthContext>
       <Layout>
          <Component {...pageProps} />
       </Layout>
     </AuthContext>
  );
}

Edit: The difference is more visible without JSX

// incorrect
return React.createElement(AuthContext, null,
  // getLayout is called before AuthContext
  getLayout(
    React.createElement(Component, pageProps)
  )
)

// correct
return React.createElement(AuthContext, null,
  // Layout is called after AuthContext
  React.createElement(Layout, null,
    React.createElement(Component, pageProps)
  )
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM