React-Redux Hooks: useSelector and useDispatch

A guide to using Redux with React hooks for cleaner code

Reinald Reynoso
Level Up Coding

--

React-Redux

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:

React-Redux Hooks Practice Gif

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 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.

counterActions.js

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.

userActions.js

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.

Actions -> index.js

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.

Counter Reducer, counter.js

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.

Current User Reducer. currentUser.js

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)
// 1
const 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}
App Component

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!

--

--