Using React Redux Hooks: useSelector() and useDispatch()

Divine Orji
June 7, 2022
Try Memberstack for Free!

TABLE OF CONTENTS
GET STARTED
Try Memberstack

React Redux useSelector is a custom hook introduced in React Redux v7.1.0. Combined with other custom hooks such as useDispatch, it enables developers to manage state in Redux while writing fast, performant, and easy-to-read code in as few lines as possible. In this article, you will explore useSelector, see how it works together with useDispatch, and know when to use it.

Introduction - React Redux Basics

React hooks are APIs that give function components the ability to manage state, handle side effects, and more while maintaining clean and concise code.

They were introduced as part of React v16.8 and have improved over time, with custom hooks created for specific use cases.

In React-Redux, before hooks' introduction, the connect() API was the higher-order component that reads values from the Redux store each time it updates.

The connect() API takes in two optional arguments:

  • mapStateToProps: Whenever there are updates to the state of the Redux store, it receives the entire state and returns an object of data needed by your component.
  • mapDispatchToProps: It can be a function or an object. As a function, it receives dispatch as an argument and returns an object of functions that dispatch actions. As an object, it contains action creators that turn to props that automatically dispatch actions when called.

With the advent of hooks, the React team created useSelector, useDispatch, and other custom hooks that make it easier to work with React-Redux and replace the connect() API.

What is useSelector?

The useSelector hook is similar to mapStateToProps. It subscribes to the Redux store, runs your provided function after each dispatch, and rerenders the component based on updates to the state.

The significant difference between them is that mapStateToProps passes down multiple values as props, while useSelector takes the current state as an argument, returns the required data, and stores the returned value as a single variable instead of a prop.

How to use useSelector hook in React

You will build a counter app for this article and implement the useSelector hook. Download the starter files here.

Unzip the project folder, navigate to it in your terminal, and run the command below to set up its existing npm packages:


yarn


or using NPM


npm install


After the packages have been installed, open the project in your favorite code editor, and view its code. Its folder structure should be similar to the one below:


todo-app-demo/
├─ node_modules/
├─ public/
├─ src/
│  ├─ App.js
│  ├─ App.test.js
│  ├─ index.css
│  ├─ index.js
│  ├─ reportWebVitals.js
│  ├─ setUpTests.js
├─ .gitignore
├─ package.json
├─ postcss.config.js
├─ README.md
├─ tailwind.config.js
├─ yarn.lock


To enable you to implement useSelector in your project, install redux and react-redux by running the command below in your terminal:


yarn add redux react-redux


After successful installation, navigate to your src/ folder and create an actions/ folder with an increment.js file. Update the file with the code below:


const increment = {
  type: 'INCREMENT',
};

export default increment;


Here you created an action with type: 'INCREMENT', to tell the reducer what action to take.

In src/, create a reducers/ folder with a counter.js file, and update it with the code below:


const counter = (state, action) => {
  if (action.type === 'INCREMENT') {
    return {
      ...state,
      value: state.value + 1,
    };
  }
  return state;
};

export default counter;

In the code above, you wrote a reducer function that takes in the state and action as arguments and updates the state’s value based on the type of action specified.

In src/, create a store.js file and update it with the code below:


Here you created a redux store using createStore() to handle your state. createStore  takes in your counter reducer function and an initialState with a value of 0.

Now, to make your store available globally, open src/index.js and update it with the code below:


import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import store from './store';

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();


Here you wrapped your <App /> in a <Provider> component from react-redux and set the value of its store to the store you created in src/store.js.

Finally, implement useSelector to get your counter value from the store. Open your src/App.js file and update it with the code below:


import { useSelector } from 'react-redux';

function App() {
  const value = useSelector((state) => state.value);

  return (
    <div className="flex flex-col space-y-5 items-center py-10">
      <h1 className="text-7xl font-bold">{value}</h1>
      <button className="p-2 bg-blue-700 hover:bg-blue-800 hover:shadow font-semibold text-white text-xl rounded-lg"
      >
        Click me
      </button>
    </div>
  );
}

export default App;


Here you created a value variable in which useSelector gets the state from your Redux store and returns state.value.

You then set the value in the <h1> tag.

If you check the result in your browser, you will see this:

Because there are no manipulations to the state yet, it only shows the initialState. To test it, open src/store.js and change the value in your initialState to any number of your choice. You will see it updated on your screen.

To handle updates to the state based on your reducer function in src/reducers/counter.js, you will implement another React Redux hook called useDispatch.

What is useDispatch?

The useDispatch hook is similar connect() API’s mapDispatchToProps.

It enables you to dispatch any action to the store by adding the action as an argument to the dispatch variable.

How to use useDispatch hook in React

Building on your example in useSelector, add useDispatch by opening your src/App.js file and updating it with the code below:


import { useDispatch, useSelector } from 'react-redux';
import increment from './actions/increment';

function App() {
  const value = useSelector((state) => state.value);

  const dispatch = useDispatch();

  const handleClick = () => {
    dispatch(increment);
  };

  return (
    <div className="flex flex-col space-y-5 items-center py-10">
      <h1 className="text-7xl font-bold">{value}</h1>
      <button
        onClick={handleClick}
        className="p-2 bg-blue-700 hover:bg-blue-800 hover:shadow font-semibold text-white text-xl rounded-lg"
      >
        Click me
      </button>
    </div>
  );
}

export default App;


Here, you created a dispatch variable that implements useDispatch(). You then created a handleClick function that dispatches the implement action you created in src/actions/increment.js so that when the user clicks the button, the handleClick function will execute.

Here is the result below:


Redux Hooks (useSelector, useDispatch) vs connect() API

According to React-Redux’s official documentation, it is recommended that you use React-Redux hooks by default; however, there are some cases where the connect() API implements a feature better due to its maturity. Some things to keep in mind when using them include:

  • Stale Props and “Zombie Children”: connect() API has an inbuilt Subscription class to ensure that connected components only receive store update notifications when the nearest connected ancestor has been updated. The connect() API overrides React’s internal context and renders <ReactReduxContext.Provider> with a new value. There is no way to render a context provider in Redux hooks, so useSelector can run before newly updated props, resulting in stale props and “zombie children.” This is an edge case.
  • Decoupling: The container logic (how data from the Redux store is injected into the component) is separated from the view logic (component rendering) with mapStateToProps. useSelector provides a fresh and different approach to connected components, stating that components are self-contained and component decoupling is more important.
  • Types: It can be challenging to use TypeScript in connect() API. It is much easier to use TypeScript with Redux hooks.
  • Developer Experience: Redux hooks don’t require you to write a lot of code. They are much easier to use than connect(), which requires you to create a container component for each connected component. Hooks are also more straightforward for beginners to understand than connect().
  • Bundle size: Redux hooks have a much smaller bundle size, making them faster to install and implement in your code.
  • The Future: As React gravitates more toward function components and hooks, Redux hooks are more future-proof than connect() API.

Conclusion

In this article, you learned to improve your coding experience by writing clean, concise code using React Redux’s useSelector and useDispatch hooks. You also learned about the differences between Redux hooks and the connect()  API and when to use them. You can find the source code used in the article on GitHub and go a step further by adding more functionality to the code. For an indepth knowledge on React-Redux hooks, check out their official documentation.