说明
本篇文章是根据React-redux中文文档学习之总结,若有不恰当之处,忘加以斧正;
前篇
上一篇中我已经介绍来Redux的一些知识概念已经总结经验,地址: Redux个人学习总结 , 这篇文章主要是讲如何在React中使用redux,redux生态中提供了Reduct-redux解决方案。
介绍
React-Redux是Redux的官方React绑定库。它能够使你的React组件从Redux store中读取数据,并且向store分发actions以更新数据
安装
npm install --save react-redux
或者
yarn add react-redux
使用方法:略
核心概念(Provider 和 connect)
Provider:
使Redux store可用于connect()调用下面组件层次结构中。通常,如果没有<Provider>包装父级或祖先组件,则无法使用connect()。<Provider/>使得每一个被connect()函数包装过的嵌套组件都可以访问到Redux store。
props:
- store(Redux Store):你应用里的唯一Redux store
- children(ReactElement):根组件
demo
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import store from "./store";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
connect(重点,做好笔记):
connect函数的主要作用是将一个React组件连接到Redux Store。
connect()(MyComponent);
// 与下面语句等价
connect(
null,
null
)(MyComponent);
connect参数(着重将前2个);
- mapStateToProps?: Function(常用)
- mapDispatchToProps?: Function | Object(常用)
- mergeProps?: Function (具体用法参考:网址)
- options?: Object (具体用法参考网址)
function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)
1. 使用使用mapStateToProps抽取数据
参数:
- state
- ownProps(可选)
mapStateToProps?: (state, ownProps?) => Object
详解:
mapStateToProps可以传递2个参数,第二个参数是可选的;
- state:如果指定了第一个参数,那么新组件就会向Redux store订阅更新。这意味着任何时候store一旦更新,mapStateToProps就会被调用。mapStateToProps的结果必须是一个纯对象,之后该对象会合并到组件的props;如果你不希望订阅store(就是不监听store中state的更新),可以将此参数设置为null或undefined;
// TodoList.js
// 在组件中可是使用props调用: 如 props.todoList
// 简写,这种方法也可以定义mapStateToProps函数
/**
* const mapStateToProps = (state) =>{
* const { todos } = state;
* return { todoList: todos.allIds };
* }
*/
function mapStateToProps(state) {
const { todos } = state;
return { todoList: todos.allIds };
};
export default connect(mapStateToProps)(TodoList);
- ownProps(可选参数) 如果组件需要用自身的props数据以从store中检索出数据,你可以传入第二个参数,简单一点理解就是:定义了这个属性可以把组件定义的属性传递进来;
// Todo.js
function mapStateToProps(state, ownProps) {
const { visibilityFilter } = state;
const { id } = ownProps;
const todo = getTodoById(state, id);
// 组件额外接收:
return { todo, visibilityFilter };
};
// 之后,在你的应用里,渲染一个如下父组件:
<ConnectedTodo id={123} />
// 你的组件接收 props.id, props.todo, 以及 props.visibilityFilter
返回值:
mapStateToProps方法应该返回一个包含了组件用到的数据的纯对象:每一个对象中的字段都将作为你组件的一个prop字段中的值用来判断你的组件是否需要重新渲染;
注意事项:
- mapStateToProps方法应该足够快;一旦store改变了,那么他的状态就会改变,如果太耗时、容易成为性能瓶颈;
- mapStateToProps方法应该纯净且同步;尽量不要再这里做异步操作;
- 使用Selector方法去抽取和转化数据;如果需要根据state生成新的对象返回给组件使用;(就是通过外部方法进行数据处理)
- 恰当的使用ownProps参数;当有(state,ownProps)两个参数时,每当store state不同、或每当包装props变化时,函数都会运行。
- 考虑使用Redux的中间件Reselect,替代state替代数据变化(Reselect这个中间件要解决的问题是:在组件交互操作的时候,state发生变化的时候如何减少渲染的压力.在Reselect中间中使用了缓存机制,链接Reselect)
2. 使用使用mapStateToProps抽取数据
mapDispatchToProps?: Object | (dispatch, ownProps?) => Object
注意:mapDispatchToProps作为connect的第二个参数,他可以是一个对象,也可以是一个函数或者为null
-
mapDispatchToProps为null时候或者connect的第二个值为空时;
// 或者 connect(mapStateToProps /** 没有第二个参数 */)(MyComponent);
一旦你以这种方式连接了你的组件,你的组件就会接收props.dispatch。你可以用它来向store中分发actions。
function Counter({ count, dispatch }) { return ( <div> <button onClick={() => dispatch({ type: "DECREMENT" })}>-</button> <span>{count}</span> <button onClick={() => dispatch({ type: "INCREMENT" })}>+</button> <button onClick={() => dispatch({ type: "RESET" })}>reset</button> </div> ); }
-
当mapDispatchToProps传递的是一个对象:其内部的每一个函数都被假定为一个Redux action 创建函数,这个对象会作为props传递给连接组件,且其内部每个字段名都与action creators相同,但被一个能调用dispatch方法的函数包装,这样一来这些分发函数就可以直接调用。
案例
import * as React from 'react' // 导入 action函数; import { addTodo, deleteTodo, toggleTodo } from './actionCreators' //组件可以通过props.addTodo 调用 class TodoApp extends React.component{ ... // 代码部分省略 ;<button onClick={() => this.props.addTodo()} /> ... } const mapDispatchToProps = { addTodo, deleteTodo, toggleTodo } export default connect( null, mapDispatchToProps )(TodoApp)
-
如果mapDispatchToProps传递是一个函数时候,该函数会有2个参数;
参数- dispatch: Function
- ownProps?: Object
-
[1] 函数第一个参数就是dispatch,我们可以使用dispatch去绑定action创建函数;
案例
class TodoApp extends React.component{ ... // 代码部分省略 const {increment,decrement,reset} = this.props; // 调用 ;<button onClick={increment} /> ;<button onClick={decrement} /> ;<button onClick={reset(1)} /> ... } // 定义 mapDispatchToProps函数,第一个参数为dispatch const mapDispatchToProps = dispatch => { return { // dispatching plain actions increment: () => dispatch({ type: 'INCREMENT' }), decrement: () => dispatch({ type: 'DECREMENT' }), reset: (id:number) => dispatch({ type: id }) } }
-
[2] 函数的第二个参数是ownProps,第二个参数就是传递给连接组件的props,同样,当对应connect绑定的组件上的值发送改变时候,mapDispatchToProps通过浅比较判断是否需要重新运行(在使用的时候需要判断是否需要此参数、否则容易多次运算)
案例
// 导入 import { toggleTodo } from './action'; // 被外部组件引用组件 ;<TodoApp todoId={123} /> //todoApp.ts ;<button onClick={() => this.props.toggleTodo(this.props.todoId)} /> // 绑定 “props” 变化 const mapDispatchToProps = (dispatch, ownProps) => { toggleTodo: () => dispatch(toggleTodo(ownProps.todoId)) } export default connect(null, mapDispatchToProps)(TodoApp)
-
如果不提供mapDispatchToProps函数,置空就行
返回值:
mapDispatchToProps函数返回一个纯对象。
- 每一个对象的字段都会作为你的组件的一个独立prop,并且字段的值通常是一个调用后能分发action的函数。
- 如果你在dispatch()中使用了action创建函数(区别于纯对象形式的action),通常约定字段名与action创建函数的名称相同
2.1 如果在用mapDispatchToProps参数的时候,Redux提供了一个函数(bindActionCreators)简化这个操作:
bindActionCreators将值为action creators的对象,转化为同键名的新对象,但将每个action creators封装到一个dispatch调用中,以便可以直接调用它们
bindActionCreators接收两个参数:
- 一个函数(action creator)或一个对象(每个属性为一个action creator)
- dispatch
import { bindActionCreators } from "redux";
const increment = () => ({ type: "INCREMENT" });
const decrement = () => ({ type: "DECREMENT" });
const reset = () => ({ type: "RESET" });
// 绑定一个action creator
// 返回 (...args) => dispatch(increment(...args))
const boundIncrement = bindActionCreators(increment, dispatch);
// 绑定一个action creators构成的object
const boundActionCreators = bindActionCreators({ increment, decrement, reset }, dispatch);
// 返回值:
// {
// increment: (...args) => dispatch(increment(...args)),
// decrement: (...args) => dispatch(decrement(...args)),
// reset: (...args) => dispatch(reset(...args)),
// }
在mapDispatchToProps中使用bindActionCreators函数:
import { bindActionCreators } from "redux";
// ...
function mapDispatchToProps(dispatch) {
return bindActionCreators({ increment, decrement, reset }, dispatch);
}
// 组件能接收到 props.increment, props.decrement, props.reset
connect(
null,
mapDispatchToProps
)(Counter);
总结
React-redux常用方法已经介绍完了;个人总结就是在React-redux中使用还是很简单的;在主入口写入之后,通过connect函数可以轻松实现state的action和dispatch;可能需要注意的就是在编写组件的时候尽量把每个的action、reducer分开起来写;方便管理;控制;