Using Redux with React Hooks

Stephen Hanson

Note, 2023: This article was most relevant when Redux initially launched its hooks API as people began migrating to hooks. While it probably isn’t as useful as it was then, it is still accurate in 2023 and relevant for anyone who is working with a legacy codebase. You might also be interested in our newer post: Redux Toolkit is the official way to write Redux apps.


React Redux recently released version 7.1, which includes long awaited support for React Hooks. This means that you can now ditch the connect higher-order component and use Redux with Hooks in your function components. This post will take a look at how to get started using Redux with Hooks and then explore some gotchas of this approach.

What are Hooks?

Hooks were added to React in 16.8 and allow you to access things like state, React lifecycle methods, and other goodies in function components that were previously only available in class components.

For example, a React class component like this:

class Count extends React.Component {
  state = {
    count: 0,
  };

  add = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.add}>Add</button>
      </div>
    );
  }
}

Could be rewritten as a function component using Hooks like this:

const Count = () => {
  // state variable, initialized to 0
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Add</button>
    </div>
  );
};

The code is more concise and allows teams to make more use of function components without having to convert them to class components as soon as they need state or access to the React lifecycle. This post isn’t really about Hooks in general, so I’d check out the excellent Hooks documentation if you’d like to learn more.

How to use Redux with Hooks

React Redux now includes its own useSelector and useDispatch Hooks that can be used instead of connect.

useSelector is analogous to connect’s mapStateToProps. You pass it a function that takes the Redux store state and returns the pieces of state you’re interested in.

useDispatch replaces connect’s mapDispatchToProps but is lighter weight. All it does is return your store’s dispatch method so you can manually dispatch actions. I like this change, as binding action creators can be a little confusing to newcomers to React Redux.

Alright, so now let’s convert a React component that formerly used connect into one that uses Hooks.

Using connect:

import React from "react";
import { connect } from "react-redux";
import { addCount } from "./store/counter/actions";

export const Count = ({ count, addCount }) => {
  return (
    <main>
      <div>Count: {count}</div>
      <button onClick={addCount}>Add to count</button>
    </main>
  );
};

const mapStateToProps = (state) => ({
  count: state.counter.count,
});

const mapDispatchToProps = { addCount };

export default connect(mapStateToProps, mapDispatchToProps)(Count);

Now, with the new React Redux Hooks instead of connect:

import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { addCount } from "./store/counter/actions";

export const Count = () => {
  const count = useSelector((state) => state.counter.count);
  const dispatch = useDispatch();

  return (
    <main>
      <div>Count: {count}</div>
      <button onClick={() => dispatch(addCount())}>Add to count</button>
    </main>
  );
};

I like that using Redux with Hooks is a little bit simpler conceptually than wrapping components in the connect higher-order component. Another benefit of not using the higher-order component is that you no longer get what I call “Virtual DOM of death”:

Deeply nested DOM tree due to components wrapped with connect function

I suggest reading the full Redux Hooks documentation for more information.

useSelector gotchas

useSelector diverges from mapStateToProps in one fairly big way: it uses strict object reference equality (===) to determine if components should re-render instead of shallow object comparison. For example, in this snippet:

const { count, user } = useSelector((state) => ({
  count: state.counter.count,
  user: state.user,
}));

useSelector is returning a different object literal each time it’s called. When the store is updated, React Redux will run this selector, and since a new object was returned, always determine that the component needs to re-render, which isn’t what we want.

The simple rule to avoid this is to either call useSelector once for each value of your state that you need:

const count = useSelector((state) => state.counter.count);
const user = useSelector((state) => state.user);

or, when returning an object containing several values from the store, explicitly tell useSelector to use a shallow equality comparison by passing the comparison method as the second argument:

import { shallowEqual, useSelector } from "react-redux";

const { count, user } = useSelector(
  (state) => ({
    count: state.counter.count,
    user: state.user,
  }),
  shallowEqual
);

There is a section in the Redux Hooks documentation that covers this in more detail.

Redux with Hooks vs. Connect higher-order component

Hooks are great. They allow for using function components in ways that weren’t previously possible, and the community is clearly moving in the direction of using function components and hooks when possible. The React documentation states:

We intend for Hooks to cover all existing use cases for classes, but we will keep supporting class components for the foreseeable future.

While there is no need to race off and convert all your existing code, our recommendation for new code is to generally prefer function components and Hooks over using class-based components and higher-order components. Using the Redux Hooks aligns with this recommendation and provides other key benefits over connect.

The main benefit of using the Redux Hooks is that they are conceptually simpler than connect. With connect, you are wrapping your component and injecting props into it. This can make it difficult to determine in the component which props come from Redux and which are passed in. If you are using TypeScript, correctly defining the types of connected components can be quite a chore for this reason. The Redux Hooks, on the other hand, are just regular hooks that don’t modify the public interface of your component. In TypeScript projects, I create my own useSelector that is typed to my store, and then I get type-checking everywhere I use it. Easy!

React Hooks are a useful new feature, and React Redux’s addition of Redux-specific hooks is a great step toward simplifying Redux development.