This example shows how to integrate Redux and Redux Thunk in Next.js.
Usually splitting your app state into pages
feels natural but sometimes you'll want to have global state for your app. This is an example on how you can use redux that also works with Next.js's universal rendering approach.
Deploy the example using ZEIT Now:
Execute create-next-app
with npm or Yarn to bootstrap the example:
npm init next-app --example with-redux-thunk with-redux-thunk-app
# or
yarn create next-app --example with-redux-thunk with-redux-thunk-app
Download the example:
curl https://codeload.github.com/zeit/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-redux-thunk
cd with-redux-thunk
Install it and run:
npm install
npm run dev
# or
yarn
yarn dev
Deploy it to the cloud with ZEIT Now (Documentation).
The Redux Provider
is implemented in pages/_app.js
. The MyApp
component is wrapped in a withReduxStore
function, the redux store
will be initialized in the function and then passed down to MyApp
as this.props.initialReduxState
, which will then be utilized by the Provider
component.
Every initial server-side request will utilize a new store
. However, every Router
or Link
action will persist the same store
as a user navigates through the pages
. To demonstrate this example, we can navigate back and forth to /show-redux-state
using the provided Link
s. However, if we navigate directly to /show-redux-state
(or refresh the page), this will cause a server-side render, which will then utilize a new store.
In the clock
component, we are going to display a digital clock that updates every second. The first render is happening on the server and then the browser will take over. To illustrate this, the server rendered clock will initially have a black background; then, once the component has been mounted in the browser, it changes from black to a grey background.
In the counter
component, we are going to display a user-interactive counter that can be increased or decreased when the provided buttons are pressed.
This example includes two different ways to access the store
or to dispatch
actions:
1.) pages/index.js
will utilize connect
from react-redux
to dispatch
the startClock
redux action once the component has been mounted in the browser.
2.) components/counter.js
and components/examples.js
have access to the redux store using useSelector
and can dispatch actions using useDispatch
from react-redux@^7.1.0
You can either use the connect
function to access redux state and/or dispatch actions or use the hook variations: useSelector
and useDispatch
. It's up to you.
This example also includes hot-reloading when one of the reducers
has changed. However, there is one caveat with this implementation: If you're using the Redux DevTools
browser extension, then all previously recorded actions will be recreated when a reducer has changed (in other words, if you increment the counter by 1 using the +1
button, and then change the increment action to add 10 in the reducer, Redux DevTools will playback all actions and adjust the counter state by 10 to reflect the reducer change). Therefore, to avoid this issue, the store has been set up to reset back initial state upon a reducer change. If you wish to persist redux state regardless (or you don't have the extension installed), then in store.js
change (line 19) store.replaceReducer(createNextReducer(initialState))
to store.replaceReducer(createNextReducer)
.