Reputation: 1386
I have a next.js App that needs to pull SESSION data in to a global context. Currently I have:
// _app.js
const App = ({ Component, pageProps }) => {
const start = typeof window !== 'undefined' ? window.sessionStorage.getItem('start') : 0;
return (
<ElapsedContext.Provider value={start}>
<Component {...pageProps} />
</ElapsedContext.Provider>
)
};
export default App;
and I'm consuming in my Component like so:
function MyComponent(props) {
const start = useContext(ElapsedContext);
return (
// something using start;
);
}
However, when I consume that context in a component, the Component renders on the page as expected, but I get Warning: Text content did not match. Server: "0" Client: "5.883333333333345"
Which I think it because it initially passes the 0, then pulls the number from SESSION storage after the window loads.
I've tried using useEffect
in the _app.js file (which fires after window is loaded) but the initial state is then not available for my component to build what it needs built on initial render...
Upvotes: 4
Views: 9291
Reputation: 5226
Next.js renders pages either during build time or it server renders the page, which means window
and therefore sessionStorage
is not available as this runs in a Node.js environment.
If you ignore this warning, React will perform a re-render after the page loads. The great thing about server rendering React is that by the time the page loads React doesn't have to perform a re-render, so when this warning shows up you want to avoid it.
Although, because sessionStorage
isn't available until the page is rendered in the browser, you'll have to wait to fill your Context until then.
Therefore, one way to avoid this error for your case would be to do something like:
// context.js
export const ElapsedContext = React.createContext(0);
export const ElapsedProvider = ({ children }) => {
const [state, setState] = React.useState(0);
React.useEffect(() => {
// on client side mount, set starting value
setState(window.sessionStorage.getItem('start'))
}, [])
return (
<ElapsedContext.Provider
value={state}
>
{children}
</ElapsedContext.Provider>
);
};
// pages/_app.js
export default function MyApp({ Component, pageProps }) {
return <ElapsedProvider><Component {...pageProps} /></ElapsedProvider>
}
// pages/index.js
export default function MyPage() {
const start = React.useContext(ElapsedContext);
if (start === 0) {
return <Loading />
}
return <MyComponentThatUsesElapsed />
}
Upvotes: 3