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?
That being said, we can easily implement Context API using React Hooks into NextJS. Let's step through the creation of a simple custom Auth to allow our NextJS app to see the logged in status of a user, and the users details throughout every component and page.
js Context and React Context. As Next. js is a framework built on top of React, we're able to leverage the latest features released by the team over at Facebook.
Disadvantages of NextJS Although NextJS is developing rapidly and many features arrive, it still has some cons and issues which you can see below: Cost of flexibility – Next JS does not provide many built-in front pages, so you have to create the whole front-end layer from the ground up.
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:
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>
);
}
<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
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.
When you talk about getInitialProps you are talking about Server Sider Render (SSR). If you don't need to do SSR, the example in Next with Context API is enough, otherwise you can use the pageProps
in the file _app.js
to initialize your Context, read more about Custom App in documentation Custom App
Note: If you're using Next.js 9.3 or newer, we recommend that you use getStaticProps or getServerSideProps instead of getInitialProps.
import { AppProvider } from '../contexts/AppProvider';
function MyApp({ Component, pageProps }) {
return (
<AppProvider initialData={pageProps?.initialData}>
<Component {...pageProps} />
</AppProvider>
);
}
export default MyApp;
Then you can initialize your Context with the data obtained by the server.
import { useState, createContext, useMemo } from 'react';
export const AppContext = createContext();
export const AppProvider = ({ children, initialData }) => {
const [data, setData] = useState(initialData);
const value = useMemo(() => ({ data, setData }), [data]);
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
};
But wait!!, how can we get the data from the server of the Page? This can be a bit confusing, but pageProps
obtained from the Page with getServerSideProps
are always passed through MyApp and then to the child component.
getServerSideProps (in Page) ===> MyApp({ Component, pageProps }) ===> Page({pageProps})
And this is what Page would look like consuming the context. The first time, the Server renders the page and initializes the Context, and then you can get the data or update the context again.
import { useContext } from 'react';
import { AppContext } from '../contexts/AppProvider';
export default function Index() {
const { data, setData } = useContext(AppContext);
const handleOnClick = () => {
setData(`Data from client: ${Date.now()}`);
};
console.log(data);
return (
<div>
<div>{JSON.stringify(data)}</div>
<button onClick={handleOnClick}>Update Context</button>
</div>
);
}
export function getServerSideProps() {
const data = `Data from server: ${Date.now()}`;
return {
props: {
initialData: data,
},
};
}
You can check that the console.log(data);
is displayed on the server and client console, but then only on the client.
You can view the example online here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With