Hydration happens after first render when navigating from a different page
See original GitHub issueWhen I navigate to a page which I added the getStaticProps wrapper to, the hydration happens after an innitial render which results in my component getting rendered breifly without the new state changes made from the dispatch done in getStaticProps, then the hydration happens and the component rerenders with the new state.
Note that this issue only happens when I navigate from a different page. When I refresh from the page directly, the hydartion happens first, as it should, and then the component is rendered.
Sorry for lack of code snippets, will add if needed.
Edit: Code was added below.
I have create a simple counter app. The count starts at 0 but it is reset to 10 inside getStaticProps.
I have added console logs inside the HYDRATE function and the counter component
The console logs are used to indication when the hydration happens and when the counter app renders
The result of refreshing the http://localhost:3000/counter page is:
HYDRATE...
render...
However, the result of loading http://localhost:3000 and then navigating to http://localhost:3000/counter by clicking a Link is:
render...
HYDRATE...
render...
The problem is that there are 2 renders. One before hydration and another one after. The expected behaviour is one render AFTER hydration, same as above.
// store/counter.js
import { createSlice } from '@reduxjs/toolkit';
import { HYDRATE } from 'next-redux-wrapper';
const slice = createSlice({
name: 'counter',
initialState: {
count: 0,
},
reducers: {
increment: (counter, action) => {
counter.count += action.payload;
},
decrement: (counter, action) => {
counter.count -= action.payload;
},
setCount: (counter, action) => {
counter.count = action.payload;
},
},
extraReducers: {
[HYDRATE]: (counter, action) => {
console.log('HYDRATE...');
counter.count = action.payload.counter.count;
},
},
});
export default slice.reducer;
export const { increment, decrement, setCount } = slice.actions;
export const selectCount = state => state.counter.count;
// store/configureStore.js
import { configureStore } from '@reduxjs/toolkit';
import { createWrapper } from 'next-redux-wrapper';
import counter from './counter';
export const wrapper = createWrapper(() =>
configureStore({
reducer: {
counter,
},
devTools: true,
})
);
// pages/_app.js
import '../styles/globals.css';
import { wrapper } from '../store/ configureStore';
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />;
}
export default wrapper.withRedux(MyApp);
// pages/index.js
import Head from 'next/head';
import Link from 'next/link';
import styles from '../styles/Home.module.css';
export default function Home() {
return (
<div className={styles.container}>
<Head>
<title>Counter App</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<div className={styles.title}>
<Link href="/counter">Go to counter</Link>
</div>
</main>
</div>
);
}
// pages/counter.js
import Head from 'next/head';
import styles from '../styles/Home.module.css';
import { wrapper } from '../store/ configureStore';
import { useDispatch, useSelector } from 'react-redux';
import { selectCount, increment, decrement, setCount } from '../store/counter';
export default function Counter() {
const dispatch = useDispatch();
const count = useSelector(selectCount);
console.log('render...');
return (
<div className={styles.container}>
<Head>
<title>Counter App</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<h1>Counter App</h1>
<h2>Count: {count}</h2>
<button onClick={() => dispatch(increment(1))}>Increment</button>
<button onClick={() => dispatch(decrement(1))}>Decrement</button>
</main>
</div>
);
}
export const getStaticProps = wrapper.getStaticProps(async ({ store }) => {
store.dispatch(setCount(10));
});
Issue Analytics
- State:
- Created 3 years ago
- Reactions:17
- Comments:42 (18 by maintainers)
Top GitHub Comments
I noticed that after routing, the state is reset and then only adds new dispatches for the new page. State not persisted from page to page. This is a big problem for me and do not understand how fix it.
I fixed my issue by implementing this line of documentation …
if (state.count) nextState.count = state.count; // preserve count value on client side navigation
,So, here’s my code …
The only question now is, should I do this to every piece of state that I have in order to preserve the value on client-side navigation?
If I have a
state.product
, I have to duplicate that line to be …if (state.product) nextState.product= state.product;
That sounds like there should be a better way to handle this.