17
votes

Working with Next.js and I am trying to save data inside Context API state after fetching them within getInitialProps, to fix props drilling.

But since getInitialProps is a static method, we can't access it via this.context. I managed to save them within componentDidMount, but in that case, the Context state is empty on the first-page load until it populates. Not sure what the best practice would be in this case. In which lifecycle should I save initial data to Context in order to have them right away like with props passing?

1
Have you solved this question? - Francis Rodrigues
Cant remember, but I dont think so. I moved to redux. Or yet better if u can do loading until data is ready. - Goran Jakovljevic
I solved this with ContextApi right now. I'm going to post an example. - Francis Rodrigues

1 Answers

20
votes

you cannot use ContextAPI in Next.js server-side (SSR), because it's against hooks rules. https://reactjs.org/warnings/invalid-hook-call-warning.html

React will run getInitialProps first, so the best solution is to fetch data in there and passing it through your Component using ContextAPI.

Let's go ahead and see it working as below:

Create your AppProvider component

Implement your context provider functions you want to pass through your React components.

For this case, we'll create our global Context provider wrapping the entire application in it.

const AppProvider = ({ children }) => {
  const [galleryData, setGalleryData] = React.useState([]);

  const handleGalleryData = galleryData => {
    setGalleryData(galleryData);
  }

  const contextProps = {
    galleryData,
    handleGalleryData
  };

  return (
    <AppContext.Provider value={contextProps}>
      {children}
    </AppContext.Provider>
  );
}

Then wrap your app with this new provider.

<AppProvider>
  <App />
</AppProvider>

And into your pages, such as index.js, try this way:

Index.getInitialProps = async (props) => {
  const { req, res, query, ...others } = props;

  // use your env variables, endpoint URIs
  // ..

  ... fetch whatever you want..
  const galleryProps = await fetch(endpoint); // isomorphic-unfetch

  return {
    galleryProps,
    query,
    ...others
  };
}

Depending on your Next.js version, you might use getServerSideProps instead of getInitialProps, but be aware of calling it by each request.

Next.js will pre-render this page on each request using the data returned by getServerSideProps Data Fetching docs

Start using ContextAPI over your components

Then in your components, you can check for this data and store it into ContextAPI

const Index = props => {
  const { galleryProps, query, ...others } = props;
  const [galleryData, setGalleryData] = useState(galleryProps);
  const { handleGalleryData, ...contextRest } = useContext(AppContext);
  ...

  // Here you're going to store data into ContextAPI appropriatly.
  useEffect(() => {
    if (typeof galleryProps === 'object' && _.keys(galleryProps).length > 0) {
      handleGalleryData(galleryProps);
    }
  }, [handleGalleryData]);

  // Other times your page is loaded, you will GET this data from ContextAPI, instead of SSR props.
  useEffect(() => {
    if (_.keys(galleryDataProps).length <= 0 && _.keys(contextRest.galleryData).length > 0) {
      setGalleryData(contextRest.galleryData);
    }
  }, []);

....

return (
  <div>
    {JSON.stringify(galleryData)}
  </div>
);

The use case above isn't the best one, but it brings an understanding of how things work with ContextAPI in Next.js applications. I'll explain it below:

  • The first useEffect() is verifying if the component received a data object from props storing it over ContextAPI.

  • The second one checks if the store got some data

You may fetch data in SSR mode over getInitialProps before your component loads.

References