前言
本文主要介绍了 react-redux 的用法,以及在各种场景下不同 API 的使用方式和区别。
本文代码是在上一篇博客的基础上进行的,有疑惑的地方可以先查看上一篇博客:轻松掌握 Redux 核心用法
本文已收录在 Github: https://github.com/beichensky/Blog 中,欢迎 Star!
一、准备工作
安装
npm install react-redux
# or
yarn add react-redux
常规用法
hooks 用法
二、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" }),
}
(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?: Object
pure?: boolean
areStatesEqual?: Function
areOwnPropsEqual?: Function
areStatePropsEqual?: Function
areMergedPropsEqual?: Function
forwardRef?: boolean
下面 改写一下 App.js 中 redux 的用法
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 的实现原理。
六、源码位置
react-redux-demo
如果有写的不对或不严谨的地方,欢迎大家能提出宝贵的意见,十分感谢。
如果喜欢或者有所帮助,欢迎 Star,对作者也是一种鼓励和支持。
前言
本文主要介绍了 react-redux 的用法,以及在各种场景下不同 API 的使用方式和区别。
本文代码是在上一篇博客的基础上进行的,有疑惑的地方可以先查看上一篇博客:轻松掌握 Redux 核心用法
一、准备工作
安装
npm install react-redux # or yarn add react-redux常规用法
Provider: 使用Provider标签包裹根组件,将store作为属性传入,后续的子组件才能获取到 store 中的state和dispatchconnect:返回一个高阶组件,用来连接React组件与Redux store,返回一个新的已与Redux store连接的组件类。hooks 用法
useDispatch:返回一个dispatch对象useSelector:接受一个函数,将函数的返回值返回出来二、Provider 的使用
根目录下
index.js文件三、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合并对象的属性值都必须是
ActionCreatordispatch不会再挂载到组件的props中传递函数类型时,接收两个参数:
dispatch和ownProps(当前组件的props,不建议使用,会导致重渲染,损耗性能),必须返回一个纯对象,这个对象会和组件的props合并3. mergeProps
mergeProps:(很少使用) 函数类型。如果指定了这个参数,mapStateToProps()与mapDispatchToProps()的执行结果和组件自身的props将传入到这个回调函数中。该回调函数返回的对象将作为props传递到被包装的组件中。你也许可以用这个回调函数,根据组件的props来筛选部分的state数据,或者把props中的某个特定变量与ActionCreator绑定在一起。如果你省略这个参数,默认情况下组件的props返回Object.assign({}, ownProps, stateProps, dispatchProps)的结果mergeProps(stateProps, dispatchProps, ownProps): props4. options
context?: Objectpure?: booleanareStatesEqual?: FunctionareOwnPropsEqual?: FunctionareStatePropsEqual?: FunctionareMergedPropsEqual?: FunctionforwardRef?: boolean下面 改写一下
App.js中redux的用法mapDispatchToProps参数不传时mapDispatchToProps参数为对象时mapDispatchToProps参数为函数时四、useDispatch 和 useSelector 的使用
上面我们在组件中使用的是
connect,但是在现在这个hooks盛行的时代,怎么能只有高阶组件呢,所以下面我们来探究一下useDispatch和useSelector的用法。改写
App.js文件使用
hooks方式改写之后,感觉简洁了不少,数据来源也很清晰。至于用connect还是hook的方式,可以根据情况自己选择。五、总结
Redux由Action、Reducer、Store组成Reducer创建Storestore.dispatch(action)触发更新函数reducerreducer更新数据subscribe通过
combineReducers可以合并多个reducer通过
applyMiddleware可以使用插件通过
bindActionCreators可以将ActionCreator转化成dispatch包装后的ActionCreator六、源码位置
react-redux-demo
如果有写的不对或不严谨的地方,欢迎大家能提出宝贵的意见,十分感谢。
如果喜欢或者有所帮助,欢迎 Star,对作者也是一种鼓励和支持。