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
Top comments (13)
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: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.
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
anduseSelector
are basically equivalent.mapDispatch
is admittedly doing a couple more steps thanuseDispatch
, but ultimately the behavior is the same (and really code like() => dispatch(increaseVote(anime.id)
is actually doing some work thatmapDispatch
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.
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!
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!
Yep, I'm literally working on a tutorial right now that teaches RTK as the default approach :)
Agreed that the code could be more brief, but it also makes the component harder to test in isolation (ie, without a complete store)
Nice comparison in pieces
I didn't know that we could use those kinda hooks ๐ฎ... It's really easier than use MDP and MSP .. Thank you!
Good
Do you any post to use Duck pattern, actually the better way to use it?
Awesome!
Thanks for sharing @jennifer !
Some comments may only be visible to logged-in visitors. Sign in to view all comments.