redux 中的 connect 用法介绍及原理解析

2023-05-27 0 398

react-redux 提供更多了三个关键的第一类, Provider 和 connect ,前者使 React 模块可被相连(connectable),前者把 React 模块和 Redux 的 store 或者说相连出来。react-redux 的文件格式中,对 connect 的叙述是几段艰涩艰涩的英语,在算数 redux 的这时候,我对着那段文件格式写作了很久,都没全数弄知道当中的原意(约莫是,单字我都重新认识,连出来兰香就不知道了的觉得吧)。

在采用了一两年 redux 后,责任编辑试著再度返回这儿,给那段文件格式(与此同时节录在第三章中)三个可信赖的阐释。

有关 react-redux 的三个时序

redux 中的 connect 用法介绍及原理解析

connect 用语如是说

connect 方式新闻稿:

connect([mapStateToProps], [mapDispatchToProps], [mergeProps],[options])

促进作用:相连 React 模块与 Redux store。

模块表明:

mapStateToProps(state, ownProps) : stateProps

那个表达式容许他们将 store 中的统计数据做为 props 存取到组件上。

const mapStateToProps = (state) => { return { count: state.count }}

(1)那个表达式的第三个模块是 Redux 的 store,他们从中摘取了 count 属性。你不必将 state 中的统计数据原封不动地传入模块,可以根据 state 中的统计数据,动态地输出模块需要的(最小)属性。

(2)表达式的第二个模块 ownProps,是模块自己的 props。有的这时候,ownProps 也会对其产生影响。

当 state 变化,或者 ownProps 变化的这时候,mapStateToProps 都会被调用,计算出三个新的 stateProps,(在与 ownProps merge 后)更新给模块。

mapDispatchToProps(dispatch, ownProps): dispatchProps

connect 的第二个模块是 mapDispatchToProps,它的功能是,将 action 做为 props 存取到模块上,也会成为 MyComp 的 props。

[mergeProps],[options]

不管是 stateProps 还是 dispatchProps,都需要和 ownProps merge 之后才会被赋给模块。connect 的第三个模块是用来做这件事。通常情况下,你可以不传那个模块,connect 就会采用 Object.assign 替代该方式。

[options] (Object) 如果指定那个模块,可以定制 connector 的行为。一般不用。

基本原理导出

首先connect之所以会成功,是因为Provider模块:

在原应用模块上包裹一层,使原来整个应用成为Provider的子模块接收Redux的store做为props,通过context第一类传递给子孙模块上的connect

那connect做了些什么呢?

它或者说相连 Redux 和 React,它包在他们的容器模块的外一层,它接收上面 Provider 提供更多的 store 里面的 state 和 dispatch,传给三个构造表达式,返回三个第一类,以属性形式传给他们的容器模块。

有关它的源码

connect 是三个高阶表达式,首先传入 mapStateToProps、mapDispatchToProps,然后返回三个生产 Component 的表达式(wrapWithConnect),然后再将或者说的Component 做为模块传入 wrapWithConnect,这样就生产出三个经过包裹的Connect模块,该模块具有如下特点:

通过 prop 传给或者说的 ComponentcomponentDidMount 时,添加事件 this.store.subscribe(this.handleChange),实现页面交互shouldComponentUpdate 时判断是否有避免进行渲染,提升页面性能,并得到 nextStatecomponentWillUnmount 时移除注册的事件 this.handleChange

由于 connect 的源码过长,他们只看主要逻辑:

export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) { return function wrapWithConnect(WrappedComponent) { class Connect extends Component { constructor(props, context) { // 从祖先Component处获得store this.store = props.store || context.store this.stateProps = computeStateProps(this.store, props) this.dispatchProps = computeDispatchProps(this.store, props) this.state = { storeState: null } // 对stateProps、dispatchProps、parentProps进行合并 this.updateState() } shouldComponentUpdate(nextProps, nextState) { // 进行判断,当统计数据发生改变时,Component重新渲染 if (propsChanged || mapStateProducedChange || dispatchPropsChanged) { this.updateState(nextProps) return true } } componentDidMount() { // 改变Component的state this.store.subscribe(() = { this.setState({ storeState: this.store.getState() }) }) } render() { // 生成包裹模块Connect return ( <WrappedComponent {…this.nextState} /> ) } } Connect.contextTypes = { store: storeShape } return Connect; } }

connect 采用实例

这儿他们写三个有关计数器采用的实例:

Component/Counter.js

import React, {Component} from reactclass Counter extends Component { render() { //从模块的props属性中导入四个方式和三个变量 const {increment, decrement, counter} = this.props; //渲染模块,包括三个数字,四个按钮 return ( <p> Clicked: {counter} times { } <button onClick={increment}>+</button> { } <button onClick={decrement}>-</button> { } </p> ) }}export default Counter;

Container/App.js

import { connect } from react-reduximport Counter from ../components/Counterimport actions from ../actions/counter;//将state.counter存取到props的counterconst mapStateToProps = (state) => { return { counter: state.counter }};//将action的所有方式存取到props上const mapDispatchToProps = (dispatch, ownProps) => { return { increment: (…args) => dispatch(actions.increment(…args)), decrement: (…args) => dispatch(actions.decrement(…args)) }};//通过react-redux提供更多的connect方式将他们需要的state中的统计数据和actions中的方式存取到props上export default connect(mapStateToProps, mapDispatchToProps)(Counter)

首先回顾一下 redux 的基本用语:

connect方式新闻稿如下:connect([mapStateToProps], [mapDispatchToProps], [mergeProps],[options]) 促进作用:相连 React 模块与 Redux store。 相连操作不会改变原来的模块类,反而返回三个新的已与 Redux store 相连的模块类。 返回值根据配置信息,返回三个注入了 state 和 action creator 的 React 模块。

[mapStateToProps(state, [ownProps]): stateProps] (Function): 如果定义该模块,模块将会监听 Redux store 的变化。任何这时候,只要 Redux store 发生改变,mapStateToProps 表达式就会被调用。该回调表达式必须返回三个纯第一类,那个第一类会与模块的 props 合并。如果你省略了那个模块,你的模块将不会监听 Redux store。如果指定了该回调表达式中的第二个参数 ownProps,则该模块的值为传递到模块的 props,而且只要模块接收到新的 props,mapStateToProps 也会被调用。

[mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function): 如果传递的是三个第一类,那么每个定义在该第一类的表达式都将被当作 Redux action creator,而且那个第一类会与 Redux store 存取在一起,当中所定义的方式名将做为属性名,合并到模块的 props 中。如果传递的是三个表达式,该表达式将接收三个 dispatch 表达式,然后由你来决定如何返回三个第一类,那个第一类通过 dispatch 表达式与 action creator 以某种方式存取在一起(提示:你也许会用到 Redux 的辅助表达式 bindActionCreators())。如果你省略那个 mapDispatchToProps 模块,默认情况下,dispatch 会注入到你的模块 props 中。如果指定了该回调表达式中第二个模块 ownProps,该模块的值为传递到模块的 props,而且只要模块接收到新 props,mapDispatchToProps 也会被调用。

[mergeProps(stateProps, dispatchProps, ownProps): props] (Function): 如果指定了那个模块,mapStateToProps() 与 mapDispatchToProps() 的执行结果和模块自身的 props 将传入到那个回调表达式中。该回调表达式返回的第一类将做为 props 传递到被包装的模块中。你也许可以用那个回调表达式,根据模块的 props 来筛选部分的 state 统计数据,或者把 props 中的某个特定变量与 action creator 存取在一起。如果你省略那个模块,默认情况下返回 Object.assign({}, ownProps, stateProps, dispatchProps) 的结果。

[options] (Object) 如果指定那个模块,可以定制 connector 的行为。

[pure = true] (Boolean): 如果为 true,connector 将执行 shouldComponentUpdate 并且浅对比 mergeProps 的结果,避免不必要的更新,前提是当前模块是三个“纯”模块,它不依赖于任何的输入或 state 而只依赖于 props 和 Redux store 的 state。默认值为 true。[withRef = false] (Boolean): 如果为 true,connector 会保存三个对被包装模块实例的引用,该引用通过 getWrappedInstance() 方式获得。默认值为 false

静态属性

WrappedComponent (Component): 传递到 connect() 表达式的原始模块类。

静态方式

模块原来的静态方式都被提升到被包装的 React 模块。

实例方式

getWrappedInstance(): ReactComponent

仅当 connect() 表达式的第四个模块 options 设置了 { withRef: true } 才返回被包装的模块实例。

备注:

1、 表达式将被调用两次。第一次是设置模块,第二次是模块与 Redux store 相连:connect(mapStateToProps, mapDispatchToProps, mergeProps)(MyComponent)。

2、 connect 表达式不会修改传入的 React 模块,返回的是三个新的已与 Redux store 相连的模块,而且你应该采用那个新模块。

3、 mapStateToProps 表达式接收整个 Redux store 的 state 做为 props,然后返回三个传入到模块 props 的第一类。该表达式被称之为 selector。
const reducer = (state = {count: 0}, action) => { switch (action.type){ case INCREASE: return {count: state.count + 1}; case DECREASE: return {count: state.count – 1}; default: return state; }}const actions = { increase: () => ({type: INCREASE}), decrease: () => ({type: DECREASE})}const store = createStore(reducer);store.subscribe(() => console.log(store.getState()));store.dispatch(actions.increase()) // {count: 1}store.dispatch(actions.increase()) // {count: 2}store.dispatch(actions.increase()) // {count: 3}

通过 reducer 创建三个 store ,每当他们在 store 上 dispatch 三个 action , store 内的统计数据就会相应地发生变化。

他们当然可以 直接 在 React 中采用 Redux:在最外层容器模块中初始化 store ,然后将 state 上的属性做为 props 层层传递下去。

class App extends Component{ componentWillMount(){ store.subscribe((state)=>this.setState(state)) } render(){ return <Comp state={this.state} onIncrease={()=>store.dispatch(actions.increase())} onDecrease={()=>store.dispatch(actions.decrease())} /> }}

但这并不是最佳的方式。最佳的方式是采用 react-redux 提供更多的 Provider 和 connect 方式。

采用 react-redux

首先在最外层容器中,把所有内容包裹在 Provider 模块中,将之前创建的 store 做为 prop传给 Provider 。

const App = () => { return ( <Provider store={store}> <Comp/> </Provider> )};

Provider 内的任何三个模块(比如这儿的 Comp ),如果需要采用 state 中的统计数据,就必须是「被 connect 过的」模块——采用 connect 方式对「你编写的模块( MyComp )」进行包装后的产物。

class MyComp extends Component { // content…}const Comp = connect(…args)(MyComp);

可见 connect 方式是重中之重。

connect 详解

究竟 connect 方式到底做了什么,他们来一探究竟。

首先看下表达式的签名:

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

connect() 接收四个模块,它们分别是 mapStateToProps , mapDispatchToProps , mergeProps 和 options 。

mapStateToProps(state, ownProps) : stateProps

那个表达式容许他们将 store 中的统计数据做为 props 存取到模块上。

const mapStateToProps = (state) => { return { count: state.count }}

这个表达式的第三个模块是 Redux 的 store ,他们从中摘取了 count 属性。因为返回了具有 count 属性的第一类,所以 MyComp 会有名为 count 的 props 字段。

class MyComp extends Component { render(){ return <div>计数:{this.props.count}次</div> }}const Comp = connect(…args)(MyComp);

当然,你不必将 state 中的统计数据原封不动地传入模块,可以根据 state 中的统计数据,动态地输出模块需要的(最小)属性。

const mapStateToProps = (state) => { return { greaterThanFive: state.count > 5 }}

表达式的第二个模块 ownProps ,是 MyComp 自己的 props 。有的这时候, ownProps 也会对其产生影响。比如,当你在 store 中维护了三个用户列表,而你的模块 MyComp 只关心三个用户(通过 props 中的 userId 体现)。

const mapStateToProps = (state, ownProps) => { // state 是 {userList: [{id: 0, name: 王二}]} return { user: _.find(state.userList, {id: ownProps.userId}) }}class MyComp extends Component { static PropTypes = { userId: PropTypes.string.isRequired, user: PropTypes.object }; render(){ return <div>用户名:{this.props.user.name}</div> }}const Comp = connect(mapStateToProps)(MyComp);

当 state 变化,或者 ownProps 变化的这时候, mapStateToProps 都会被调用,计算出三个新的 stateProps ,(在与 ownProps merge 后)更新给 MyComp 。

这是将 Redux store 中的统计数据相连到模块的基本方式。

mapDispatchToProps(dispatch, ownProps): dispatchProps

connect 的第二个模块是 mapDispatchToProps ,它的功能是,将 action 做为 props 存取到 MyComp 上。

const mapDispatchToProps = (dispatch, ownProps) => { return { increase: (…args) => dispatch(actions.increase(…args)), decrease: (…args) => dispatch(actions.decrease(…args)) }}class MyComp extends Component { render(){ const {count, increase, decrease} = this.props; return (<div> <div>计数:{this.props.count}次</div> <button onClick={increase}>增加</button> <button onClick={decrease}>减少</button> </div>) }}const Comp = connect(mapStateToProps, mapDispatchToProps)(MyComp);

由于 mapDispatchToProps 方式返回了具有 increase 属性和 decrease 属性的第一类,这三个属性也会成为 MyComp 的 props 。

如上所示,调用 actions.increase() 只能得到三个 action 第一类 {type:INCREASE} ,要触发那个 action 必须在 store 上调用 dispatch 方式。 diapatch 正是 mapDispatchToProps 的第三个模块。但是,为了不让 MyComp 模块感知到 dispatch 的存在,他们需要将 increase 和 decrease 三个表达式包装一下,使之成为直接可被调用的表达式(即,调用该方式就会触发 dispatch)。

Redux 本身提供更多了 bindActionCreators 表达式,来将 action 包装成直接可被调用的表达式。

import {bindActionCreators} from redux;const mapDispatchToProps = (dispatch, ownProps) => { return bindActionCreators({ increase: action.increase, decrease: action.decrease });}

同样,当 ownProps 变化的这时候,该表达式也会被调用,生成三个新的 dispatchProps ,(在与 statePrope 和 ownProps merge 后)更新给 MyComp 。注意, action 的变化不会引起上述过程,默认 action 在模块的生命周期中是固定的。

[mergeProps(stateProps, dispatchProps, ownProps): props]

之前说过,不管是 stateProps 还是 dispatchProps ,都需要和 ownProps merge 之后才会被赋给 MyComp 。 connect 的第三个模块是用来做这件事。通常情况下,你可以不传那个模块, connect 就会采用 Object.assign 替代该方式。

其他

最后还有三个 options 选项,比较简单,基本上也不大会用到(尤其是你遵循了其他的一些 React 的「最佳实践」的这时候),责任编辑就略过了。希望了解的同学可以直接看文件格式。

举报/反馈

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务