-
Notifications
You must be signed in to change notification settings - Fork 12
十分钟学会 react-redux #8
Description
前言
本文主要介绍了 react-redux 的用法,以及在各种场景下不同 API 的使用方式和区别。
本文代码是在上一篇博客的基础上进行的,有疑惑的地方可以先查看上一篇博客:轻松掌握 Redux 核心用法
本文已收录在
Github: https://github.com/beichensky/Blog 中,欢迎 Star!
一、准备工作
安装
npm install react-redux
# or
yarn add react-redux常规用法
-
Provider: 使用Provider标签包裹根组件,将store作为属性传入,后续的子组件才能获取到 store 中的state和dispatch -
connect:返回一个高阶组件,用来连接React组件与Redux store,返回一个新的已与Redux store连接的组件类。
hooks 用法
-
useDispatch:返回一个dispatch对象 -
useSelector:接受一个函数,将函数的返回值返回出来
二、Provider 的使用
根目录下 index.js 文件
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import App from "./App";
import store from "./store";
import "./index.css";
ReactDOM.render(
<React.StrictMode>
{/* 使用 Provider 标签包裹住根组件,并将 store 作为参数传入 */}
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById("root")
);三、connect 的使用
API:connect([mapStateToProps],[mapDispatchToProps],[mergeProps],[options])
connect 的用法相对复杂一些,接受四个参数,返回的是一个高阶组件。用来连接当前组件和 Redux store。
1. mapStateToProps
mapStateToProps:函数类型,接受两个参数: state 和 ownProps(当前组件的 props,不建议使用,会导致重渲染,损耗性能),必须返回一个纯对象,这个对象会与组件的 props 合并
(state[, ownProps]) => ({ count: state.count, todoList: state.todos })
2. mapDispatchToProps
mapDispatchToProps:object | 函数
- 不传递这个参数时,
dispatch会默认挂载到组件的的props中 - 传递
object类型时,会把object中的属性值使用dispatch包装后,与组件的props合并
{
increment: () => ({ type: "INCREMENT" }),
decrement: () => ({ type: "DECREMENT" }),
}-
对象的属性值都必须是
ActionCreator -
dispatch不会再挂载到组件的props中 -
传递函数类型时,接收两个参数:
dispatch和ownProps(当前组件的props,不建议使用,会导致重渲染,损耗性能),必须返回一个纯对象,这个对象会和组件的props合并
(state[, ownProps]) => ({
dispatch,
increment: dispatch({ type: "INCREMENT" }),
decrement: dispatch({ type: "DECREMENT" })
})3. mergeProps
mergeProps:(很少使用) 函数类型。如果指定了这个参数,mapStateToProps()与 mapDispatchToProps()的执行结果和组件自身的 props 将传入到这个回调函数中。该回调函数返回的对象将作为 props 传递到被包装的组件中。你也许可以用这个回调函数,根据组件的 props 来筛选部分的 state 数据,或者把 props 中的某个特定变量与 ActionCreator 绑定在一起。如果你省略这个参数,默认情况下组件的 props 返回 Object.assign({}, ownProps, stateProps, dispatchProps) 的结果
mergeProps(stateProps, dispatchProps, ownProps): props
4. options
context?: Objectpure?: booleanareStatesEqual?: FunctionareOwnPropsEqual?: FunctionareStatePropsEqual?: FunctionareMergedPropsEqual?: FunctionforwardRef?: boolean
下面 改写一下 App.js 中 redux 的用法
mapDispatchToProps参数不传时
import React, { useEffect, useCallback, useState, useMemo } from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import { actionCreators } from "./store";
function App({ count, todos, dispatch }) {
const [value, setValue] = useState("");
// 生成包装后的 actionCreator,执行之后就会触发 store 数据的更新
const { increment, decrement, getAsyncTodos, addTodo } = useMemo(
() => bindActionCreators(actionCreators, dispatch),
[dispatch]
);
// 初始化 TodoList
useEffect(() => {
getAsyncTodos();
}, [getAsyncTodos]);
const add = useCallback(() => {
if (value) {
// 分发 action
addTodo(value);
setValue("");
}
}, [value, addTodo]);
return (
<div className="App">
<h1>Hello Redux</h1>
<p>count: {count}</p>
<button onClick={increment}>increment</button>
<button onClick={decrement}>decrement</button>
<br />
<br />
<input
placeholder="请输入待办事项"
value={value}
onChange={e => setValue(e.target.value)}
/>
<button onClick={add}>add</button>
<ul>
{todos.map(todo => (
<li key={todo}>{todo}</li>
))}
</ul>
</div>
);
}
// count、todos 也会被挂载到组件的 props 中
const mapStateToProps = ({ count, todos }) => ({ count, todos });
// 第二个参数没有传递,dispatch 默认会挂载到组件的 props 中
export default connect(mapStateToProps)(App);mapDispatchToProps参数为对象时
import React, { useEffect, useCallback, useState } from "react";
import { connect } from "react-redux";
import { actionCreators } from "./store";
function App({ count, todos, increment, decrement, getAsyncTodos, addTodo }) {
const [value, setValue] = useState("");
// 初始化 TodoList
useEffect(() => {
getAsyncTodos();
}, [getAsyncTodos]);
const add = useCallback(() => {
if (value) {
// 分发 action
addTodo(value);
setValue("");
}
}, [value, addTodo]);
return (
<div className="App">
<h1>Hello Redux</h1>
<p>count: {count}</p>
<button onClick={increment}>increment</button>
<button onClick={decrement}>decrement</button>
<br />
<br />
<input
placeholder="请输入待办事项"
value={value}
onChange={e => setValue(e.target.value)}
/>
<button onClick={add}>add</button>
<ul>
{todos.map(todo => (
<li key={todo}>{todo}</li>
))}
</ul>
</div>
);
}
// count、todos 会被挂载到组件的 props 中
const mapStateToProps = ({ count, todos }) => ({ count, todos });
// actionCreators 中的 actionCreator 会被 dispatch 进行包装,之后合并到组建的 props 中去
const mapDispatchToProps = { ...actionCreators };
export default connect(mapStateToProps, mapDispatchToProps)(App);mapDispatchToProps参数为函数时
import React, { useEffect, useCallback, useState } from "react";
import { connect } from "react-redux";
import { actionCreators } from "./store";
import { bindActionCreators } from "redux";
function App({
count,
todos,
increment,
decrement,
getAsyncTodos,
addTodo,
dispatch
}) {
const [value, setValue] = useState("");
// 初始化 TodoList
useEffect(() => {
getAsyncTodos();
}, [getAsyncTodos]);
const add = useCallback(() => {
if (value) {
// 分发 action
addTodo(value);
setValue("");
}
}, [value, addTodo]);
return (
<div className="App">
<h1>Hello Redux</h1>
<p>count: {count}</p>
<button onClick={increment}>increment</button>
<button onClick={decrement}>decrement</button>
<br />
<br />
<input
placeholder="请输入待办事项"
value={value}
onChange={e => setValue(e.target.value)}
/>
<button onClick={add}>add</button>
<ul>
{todos.map(todo => (
<li key={todo}>{todo}</li>
))}
</ul>
</div>
);
}
// count、todos 会被挂载到组件的 props 中
const mapStateToProps = ({ count, todos }) => ({ count, todos });
// mapDispatchToProps 为函数时,actionCreators 中的 actionCreator 需要自己处理,返回的对象会被合并到组件的 props 中去
const mapDispatchToProps = dispatch => ({
dispatch,
...bindActionCreators(actionCreators, dispatch)
});
export default connect(mapStateToProps, mapDispatchToProps)(App);- 如果不需要更新 store 中的数据,则不需要传 mapDispatchToProps 参数
- 如果不需要自己控制 dispatch,则传递 ActionCreators 对象即可
- 如果需要自己完全控制,则传递一个回调函数
虽然上面使用
connect是在class组件,但是在函数组件中依然适用。
四、useDispatch 和 useSelector 的使用
上面我们在组件中使用的是 connect,但是在现在这个 hooks 盛行的时代,怎么能只有高阶组件呢,所以下面我们来探究一下 useDispatch 和 useSelector 的用法。
改写 App.js 文件
import React, { useEffect, useCallback, useState, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux";
import { bindActionCreators } from "redux";
import { actionCreators } from "./store";
function App() {
const [value, setValue] = useState("");
// 从 useDispatch 中获取 dispatch
const dispatch = useDispatch();
// 生成包装后的 actionCreator,执行之后就会触发 store 数据的更新
const { increment, decrement, getAsyncTodos, addTodo } = useMemo(
() => bindActionCreators(actionCreators, dispatch),
[dispatch]
);
// 通过 useSelector 获取需要用到 state 值
const { count, todos } = useSelector(({ count, todos }) => ({
count,
todos
}));
// 初始化 TodoList
useEffect(() => {
getAsyncTodos();
}, [getAsyncTodos]);
const add = useCallback(() => {
if (value) {
// 分发 action
addTodo(value);
setValue("");
}
}, [value, addTodo]);
return (
<div className="App">
<h1>Hello Redux</h1>
<p>count: {count}</p>
<button onClick={increment}>increment</button>
<button onClick={decrement}>decrement</button>
<br />
<br />
<input
placeholder="请输入待办事项"
value={value}
onChange={e => setValue(e.target.value)}
/>
<button onClick={add}>add</button>
<ul>
{todos.map(todo => (
<li key={todo}>{todo}</li>
))}
</ul>
</div>
);
}
export default App;使用 hooks 方式改写之后,感觉简洁了不少,数据来源也很清晰。至于用 connect 还是 hook 的方式,可以根据情况自己选择。
五、总结
-
Redux由Action、Reducer、Store组成- 通过
Reducer创建Store - 通过
store.dispatch(action)触发更新函数reducer - 通过
reducer更新数据 - 数据更新触发订阅
subscribe
- 通过
-
通过
combineReducers可以合并多个reducer -
通过
applyMiddleware可以使用插件 -
通过
bindActionCreators可以将ActionCreator转化成dispatch包装后的ActionCreator
是不是还没有看过瘾呢?没有的话请看我的下一篇博客,详细讲解了
Redux以及React-Redux的实现原理。
六、源码位置
如果有写的不对或不严谨的地方,欢迎大家能提出宝贵的意见,十分感谢。
如果喜欢或者有所帮助,欢迎 Star,对作者也是一种鼓励和支持。