DEV Community

loading...
Cover image for React Redux with hooks

React Redux with hooks

jenkens profile image Jen Kennedy ・3 min read

If you're not familiar with Redux or hooks feel free to check out my other articles explaining the topics and then come back! When I was first learning Redux I found all the moving parts and files incredibly hard to wrap my head around. Surprisingly, React hooks made the process of using Redux a lot easier for me. Hooks allow us to write smaller and sometimes easier to read functional components and with Redux hooks we can eliminate the tedious and confusing connect, mapStateToProps, and mapDispatchToProps.

Connecting your app to the Redux store still follows the same process with hooks as it does without hooks. You will need to create a store, which accepts a reducer, and pass that store to the Provider component that will wrap your app. The main difference with hooks comes in connecting specific components to the store to access state.

Before hooks if we wanted a component to have access to the store we needed to use the connect higher-order component.

import {connect} from 'react-redux'

export default connect()(Animes)

Now our Animes component has access to the store and if we wanted state or the ability to change the state we would have to mapStateToProps and mapDispatchToProps

import { increaseVote, decreaseVote } from '../actions';
import { connect } from 'react-redux';

const mapStateToProps = state => {
   return {
      animes: state.animes,
   };
};

const mapDispatchToProps = dispatch => {
   return {
      increaseVote: id => dispatch(increaseVote(id)),
      decreaseVote: id => dispatch(decreaseVote(id)),
   };
};

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

For me, it was pretty easy to mess up these lines or even forget to add them in the first place! Compare the above non-hooks version to the hooks version below.

import { useSelector, useDispatch } from 'react-redux';
import { INCREASE_VOTE as increaseVote } from '../actions';

const dispatch = useDispatch();
const animes = useSelector(state => state.animes);
<button onClick={() => dispatch(increaseVote)}>Increase Vote</button>

We can use the useSelector hook to access the state of the store, instead of mapStateToProps. useSelector takes the current state of the store as a parameter and returns a piece of state you want. A potential hurdle with useSelector is that it uses strict equality, different from the previous mapStateToProps, which checked if the fields changed. This can cause potential problems when trying to return an object from useSelector, so it's best practice to call useSelector once for each value of your state. Instead of using mapDispatchToProps we can use the useDispatch hook and individually dispatch any actions we need to the reducer. To get the overall picture of hooks versus non-hooks here is the same component written in both ways.

No-Hooks

import React from 'react';
import { increaseVote, decreaseVote } from '../actions';
import { connect } from 'react-redux';

const AnimeCard = ({ anime, increaseVote, decreaseVote, animesInStore }) => {
   return (
      <div className="card">
         <p>{Object.keys(animesInStore).length}</p>
         <h2>Name: {anime.name}</h2>
         <p>Votes: {anime.votes}</p>
         <img src={anime.image} alt={anime.name}></img>
         <br />
         <button
            onClick={() => {
               increaseVote(anime.id);
            }}
         >
            UpVote
         </button>
         <button
            onClick={() => {
               decreaseVote(anime.id);
            }}
         >
            DownVote
         </button>
      </div>
   );
};

const mapStateToProps = state => {
   return {
      animesInStore: state.animes,
   };
};

const mapDispatchToProps = dispatch => {
   return {
      increaseVote: id => dispatch(increaseVote(id)),
      decreaseVote: id => dispatch(decreaseVote(id)),
   };
};

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

With Hooks

import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increaseVote, decreaseVote } from '../actions';

const AnimeCard = ({ anime }) => {
   const dispatch = useDispatch();
   const animesInStore = useSelector(state => state.animes);
   return (
      <div className="card">
         <p>{Object.keys(animesInStore).length}</p>
         <h2>Name: {anime.name}</h2>
         <p>Votes: {anime.votes}</p>
         <img src={anime.image} alt={anime.name}></img>
         <br />
         <button
            onClick={() => {
               dispatch(increaseVote(anime.id));
            }}
         >
            UpVote
         </button>
         <button
            onClick={() => {
               dispatch(decreaseVote(anime.id));
            }}
         >
            DownVote
         </button>
      </div>
   );
};

export default AnimeCard;

Not only did we save about 10 lines of code, personally I think it became a lot easier to read and write. And because we aren’t using the connect higher-order component anymore our render tree is much cleaner. I hope you enjoyed this blog post and are thinking about using Redux with hooks in one of your upcoming projects. Feel free to comment with any questions!

Favorite Resources:
React-Redux docs
Using Redux with React Hooks article

Discussion (13)

pic
Editor guide
Collapse
markerikson profile image
Mark Erikson

Glad to know that our React-Redux hooks API is working well for you!

Out of curiosity, was there anything specific about connect that you felt hard to work with?

Note that the mapDispatch declaration can be shortened using the "object shorthand" form:

const mapDispatch = {increaseVote, decreaseVote};
Enter fullscreen mode Exit fullscreen mode
Collapse
jenkens profile image
Jen Kennedy Author

Thank you for all you do for Redux! My biggest struggle with using connect was setting up the mapDispatchToProps function. I'm not sure why maybe I was just so overwhelmed with all the other moving pieces of Redux that it was the straw that broke the camel's back. With hooks, it felt a bit more natural and easy to understand for me. I want this state so just use the hook to grab a specific piece and then I want to dispatch an action so just call dispatch with a specific action. Fewer steps made it a bit easier for me to work with since it was broken down into more manageable pieces.

Collapse
markerikson profile image
Mark Erikson

Yeah, I get what you're saying. It's just interesting to see how connect is sometimes described as being a lot harder to understand. mapState and useSelector are basically equivalent. mapDispatch is admittedly doing a couple more steps than useDispatch, but ultimately the behavior is the same (and really code like () => dispatch(increaseVote(anime.id) is actually doing some work that mapDispatch did for you.)

FWIW, I'm working on a major rewrite of the Redux core docs, and my next step is to add a new tutorial page that teaches Redux Toolkit and the React-Redux hooks as the default way to use Redux. It'll be interesting to see how that turns out.

Thread Thread
ninjasun profile image
ninjasun

I think because 'connect' is actually where the magic happen. We do not see any code but our components gets store and functions from redux. I think also from the javascript point of view it's not common and easy to read somethings like function(function, function)(function)
Can't wait to use the toolkit in my next project!

Collapse
rphlmr profile image
Raphaël Moreau

Redux toolkit is awesome (love createSlice). It should be the way to go, to show people that redux in 2020, is easy. In a production project, I rewrite every redux part with it. Huge productivity improvment!

Collapse
Collapse
weeksling profile image
Matthew Weeks

Agreed that the code could be more brief, but it also makes the component harder to test in isolation (ie, without a complete store)

Collapse
miteshkamat27 profile image
Mitesh Kamat

Nice comparison in pieces

Collapse
harleypasoz profile image
HarleyPasoz

I didn't know that we could use those kinda hooks 😮... It's really easier than use MDP and MSP .. Thank you!

Collapse
fredojbg profile image
Collapse
fredojbg profile image
Alfredo Jose

Do you any post to use Duck pattern, actually the better way to use it?

Collapse
rupeshiya profile image
Rupesh Krishna Jha

Awesome!
Thanks for sharing @jennifer !

Collapse
rajnishkatharotiya profile image
Comment marked as low quality/non-constructive by the community. View Code of Conduct
Rajnish Katharotiya

Great Article, Check this video:- youtu.be/K19UywclQqM.
I’ve tried to share similar knowledge here. If you found it informative please share it other and subscribe to my channel to support. Thanks.