React-Redux Hooks: useSelector and useDispatch
A guide to using Redux with React hooks for cleaner code
React Redux offers a set of Hooks as an alternative to the existing connect()
higher order component. These Hooks allow you to connect to the Redux store and dispatch actions without having to wrap your components in connect()
.
This guide will cover how to implement the React-Redux Hooks, useSelector
and useDispatch
, in your application.
Getting started
Begin by installing the following in your app:
npm install reduxnpm install react-redux
The examples in this guide will be referring to my repository:
Here is a quick gif of the app’s functionality:
There are two separate states, one for keeping track of the counter and one for keeping track of a logged-in user. We will have separate files to handle each state.
Create an actions and reducers folder
Actions
Let’s begin with defining the actions for the counter in counterActions.js
. A couple of methods are necessary, increment
and decrement
. We will export these two methods in an object.
Similarly, let’s define the actions for the current user in userActions.js
, which will also have two methods, setUser
and logOut
, that will be exported in an object.
To be organized, we import these two files under one location, the index.js
file within the actions
folder. We create a variable allActions
and set it to an object that contains the imported actions which will be exported.
Reducers
Similar to the approach taken for the actions file structure, we create separate reducers
folder to hold the user and the counter reducers. Let’s start with the counter reducer, counter.js
.
A reducer function takes in two arguments, state
and the action
. State does not necessarily have to be set to an object. In this case, the default value of the state is set to an integer.
As we defined earlier, an action returns an object that can contain two keys, type
and optionally a payload
. Based on the action type, the value of the state will be changed. Keep in mind that a default case is necessary in the event that an action type is called that does not exist to prevent the app from breaking.
For the current user reducer, currentUser.js
, the state will be set to an empty object that will contain the keys user
and loggedIn
. Notice the difference in what is being returned between counter
and currentUser
. In all cases, the counter reducer returns an integer since its initial value was an integer. In the case of the current user reducer, an object is always returned.
We need to combine these reducers into one. Under reducers/index.js
, let’s import the reducer files as well as combineReducers
:
import {combineReducers} from 'redux'
Combine reducers does what its name implies and combines the separate reducer files into one. It takes in one argument, an object that contains the reducer files. Now that our actions and reducers are set, let’s move on to implementing Redux in our app.
Implement Redux
In our index.js
under the src
folder, we will import the following:
import {Provider} from 'react-redux';
import {createStore} from 'redux'
import rootReducer from './reducers'
A Redux store will be created with the method createStore
. It takes in two arguments, the rootReducer
which is the file that combined our reducers and the Redux devtools extension.
Finally, we wrap our App
component with the Provider
component from react-redux
. The Provider
component will take in one prop, store which will be set to the store
from createStore
.
Implement useSelector/useDispatch
We import the following hooks from react-redux, useSelector
and useDispatch
. Before, we had to import connect()
from react-redux and wrap our components with it in order to map state to props and map dispatch to props.
useSelector
The equivalent of map state to props is useSelector
. It takes in a function argument that returns the part of the state that you want. In this case, we have the following keys from state defined, counter
and currentUser
. We defined these earlier when combining reducers.
const counter = useSelector(state => state.counter)
// 1const currentUser = useSelector(state => state.currentUser)
// {}
Thus the variables counter
and currentUser
are set to the state defined from their respective reducers.
useDispatch
The equivalent of map dispatch to props is useDispatch
. We will invoke useDispatch
and store it to a variable, dispatch
. Dispatch will work with the allActions
imported from the actions folder. For example, useEffect
calls a dispatch
with the following action, allActions.userActions.setUser(user)
. User is defined as:
const user = {name: "Rei"}
Keep in mind, allActions
is an object with userActions
and counterActions
as keys. Just as a refresher on the setUser
function defined in userActions.js
:
const setUser = (userObj) => { return { type: "SET_USER", payload: userObj }}
setUser
will return an object with type
and payload
. Dispatch will take this object and look through the reducers that match the action type. In this particular case, it is located currentUser.js
within reducers
folder.
case "SET_USER": return { ...state, user: action.payload, loggedIn: true}
And there you have it. The React-Redux hooks useSelector
and useDispatch
are implemented in the React App. Compared to the connect()
alternative, the code is cleaner and more organized.
Thank you for reading!
Level Up Coding
Thanks for being a part of our community! Level Up is transforming tech recruiting. Find your perfect job at the best companies.