React Redux 与 hooks
如果你还不熟悉Redux或Hooks,可以先看看我之前写的其他文章,里面解释了这些主题,然后再回来阅读!我刚开始学习 Redux 的时候,觉得它涉及的各种组件和文件让人难以理解。但出乎意料的是,React Hooks 让 Redux 的使用变得简单多了。Hooks 允许我们编写更小、有时也更容易阅读的函数式组件,而 Redux Hooks 则可以让我们省去繁琐且令人困惑的 connect、mapStateToProps 和 mapDispatchToProps 方法。
使用 Hooks 将应用连接到 Redux store 的过程与不使用 Hooks 的过程基本相同。你需要创建一个接受 reducer 的 store,并将该 store 传递给用于封装应用的 Provider 组件。使用 Hooks 的主要区别在于,它可以将特定的组件连接到 store 以访问状态。
在 Hooks 出现之前,如果我们想让一个组件访问 store,我们需要使用 connect 高阶组件。
import {connect} from 'react-redux'
export default connect()(Animes)
现在我们的动漫组件可以访问 store,如果我们想要获取状态或更改状态,我们需要使用 mapStateToProps 和 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);
对我来说,这些代码很容易出错,甚至一开始就忘记添加它们!请对比上面未使用 hooks 的版本和下面使用 hooks 的版本。
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>
我们可以使用 `useSelector` hook 来访问 store 的状态,而不是使用 `mapStateToProps`。`useSelector` 接受 store 的当前状态作为参数,并返回你想要获取的状态片段。`useSelector` 的一个潜在问题是它使用严格相等性判断,这与之前的 `mapStateToProps` 不同,后者会检查字段是否发生变化。这可能会导致在尝试从 `useSelector` 返回对象时出现问题,因此最佳实践是针对状态的每个值调用一次 `useSelector`。我们可以使用 `useDispatch` hook 来代替 `mapDispatchToProps`,并将需要的任何 action 单独分发给 reducer。为了更好地理解 hook 和非 hook 的区别,这里展示了同一个组件的两种写法。
无钩
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);
带钩子
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;
我们不仅节省了大约 10 行代码,而且我个人认为代码的可读性和可写性都大大提升。由于我们不再使用 connect 的高阶组件,渲染树也更加简洁。希望您喜欢这篇博文,并考虑在即将开展的项目中尝试使用 Redux 和 hooks。如有任何疑问,欢迎留言!
推荐资源:
React-Redux文档,
《将 Redux 与 React Hooks 结合使用》文章