In this article, I will go over how you can configure your Next JS application to use redux.
First set up your next js application
yarn create next-app next-redux
Next we install the desired packages
yarn add redux redux-thunk next-redux-wrapper react-redux
After that installation, create a redux folder in the root of your project, inside that folder add a types.js and store.js files, also add folders for actions and reducers.
In the types file, lets add a single type; for setting the name of the user
export const SET_NAME = "SET_NAME"
In the reducers folder, add a main.js
file, In this file we will create a reducer that will manage the main state of our app.
In this file we will initialize a main state with only one value for the name that defaults to guest. Then we will use a switch statement to detect the passed in action and value, the state gets updated based on the value received.
import * as t from "../types";
const main = (state = {
name: "guest",
}, action) => {
switch(action.type){
case t.SET_NAME:
return {
...state,
name: action.payload
};
default:
return {...state};
}
}
export default main;
In the same reducers folder, we will add a rootReducer.js
file, this file will combine all our reducers into one, it is most useful when you have multiple reducer files, for this article i will only be using a single reducer file, which is my main reducer.
import { combineReducers } from "redux"
import main from "./main"
const rootReducer = combineReducers({
main: main
})
export default rootReducer;
Next, we move to our store.js
file
In this file we will create our redux store using redux
and next-redux-wrapper
, we will also add redux-thunk
to allow us have extra functions before dispatching new values to our state.
import { createStore, applyMiddleware, compose } from "redux"
import thunk from "redux-thunk"
import { createWrapper } from "next-redux-wrapper"
import rootReducer from "./reducers/rootReducer"
const middleware = [thunk]
const makeStore = () => createStore(rootReducer, compose(applyMiddleware(...middleware)))
export const wrapper = createWrapper(makeStore)
Now we will create our set name action, create a main.js
file in the actions folder, inside it we will have a function that specifies the set name type with a new name value.
import * as t from "../types";
import axios from "axios";
import { request } from "../../util/request";
export const setInfo = (name) => dispatch => {
dispatch({
type: t.SET_NAME,
payload: name
});
}
After you've done all of this, our directory should look like below
Now move to your _app.js
file, it should look like;
import '../styles/globals.css'
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
Update it to look like;
import React from "react"
import { wrapper } from "../redux/store"
const MyApp = ({ Component, pageProps}) => (
<Component {...pageProps} />
)
export default wrapper.withRedux(MyApp);
We've wrapped the app in the redux wrapper we created in our store file and passed props to it.
In our index.js
, take out all the page content and leave an input box asking for a name.
return (
<div className={styles.container}>
<p>Enter a Name :</p>
<input
type="text">
</input>
<button>
Submit
</button>
</div>
)
Next we add some state management for updating and storing the value of our form using useState
, we also link our index page to our redux state and finally connect the actions using mapDispatchToProps
and the state using mapStateToProps
, the final index.js
should look like below
import { useState } from 'react'
import { connect } from "react-redux"
import { setInfo } from "../redux/actions/main"
import styles from '../styles/Home.module.css'
function Home(props) {
const { name, setInfo } = props
const [newName, setName] = useState("")
return (
<div className={styles.container}>
<p>Enter a Name {name}:</p>
<input
type="text"
value={newName}
onChange={(e) => setName(e.target.value)}>
</input>
<button onClick={() => setInfo(newName)}>
Submit
</button>
</div>
)
}
const mapStateToProps = state => {
return { name: state.main.name }
}
const mapDispatchToProps = {
setInfo
}
export default connect(mapStateToProps, mapDispatchToProps)(Home)
To be able to debug with Redux Dev Tools, update your store.js
code to;
import { createStore, applyMiddleware, compose } from "redux"
import thunk from "redux-thunk"
import { createWrapper } from "next-redux-wrapper"
import rootReducer from "./reducers/rootReducer"
const middleware = [thunk]
const composeEnhancers =
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const enhancer = composeEnhancers(applyMiddleware(...middleware));
const makeStore = () => createStore(rootReducer, enhancer)
export const wrapper = createWrapper(makeStore)
Your home page should now look like the below image, and changing the name should update the value of "guest"
If you find this article helpful, you can also checkout other Next JS videos on Youtube or the Full Stack Next JS Course on Udemy
Top comments (8)
is there any specific reason to access redux state/store (using connect) like the way you did? why dont you use useSelector hooks from react-redux instead? and als useDispatch? is there any drawbacks using those two in nextjs?
Found this helpful while building my App, I;m new to Next and would like to know if there's a particular usecase for getServerSideProps and getStaticProps while building a next-redux application
i can't access redux store on getServerSideProps, pls help
Can I see your repo?
Great Job. Do you have a repo with this code, please
github.com/theallegrarr/udemy_next
thanks <3
Awsome!! Simple