Last Surprise Lyn
  1. 1 Last Surprise Lyn
  2. 2 Life Will Change Lyn
  3. 3 Warcry mpi
  4. 4 Libertus Chen-U
  5. 5 かかってこいよ NakamuraEmi
  6. 6 Hypocrite Nush
  7. 7 One Last You Jen Bird
  8. 8 Flower Of Life 发热巫女
  9. 9 BREAK IN TO BREAK OUT Lyn
  10. 10 Time Bomb Veela
2017-12-31 12:31:09

react-redux思想概览

实际上React和Redux并没有直接的联系,前者负责视图渲染,后者负责状态管理,单独抽出使用都是可以的,但无疑两者结合可以工作的更好。为了使状态和视图更好的连接,就要使用react-redux作为胶水层,其核心方法只有两个,Provider组件和connect方法,下面就分析其原理。


Provider

该组件的作用很简单,就是接受Redux的store,作为中转为子组件提供store的context。源码如下:

export default class Provider extends Component {
  getChildContext() {    
    return { store: this.store }
  }
  constructor(props, context) {
    super(props, context)
    this.store = props.store    
  }
  render() {
    return Children.only(this.props.children)
  }
}
if (process.env.NODE_ENV !== 'production') {
  Provider.prototype.componentWillReceiveProps = function (nextProps) {
    const { store } = this
    const { store: nextStore } = nextProps
    if (store !== nextStore) {
      warnAboutReceivingStore()
    }
  }
}
Provider.propTypes = {
  store: storeShape.isRequired,
  children: PropTypes.element.isRequired
}
Provider.childContextTypes = {   
  store: storeShape.isRequired
}

首先在初始化时,获取到props中的store,然后通过react提供的getChildContext方法和childContextTypes验证向context内写入store以便子组件通过context获取。然后在render时,将当前组件的所有子组件渲染,Chirdren为react定义的顶级对象,该对象的only方法用于获取仅有的一个子组件,所以需要确保Provider组件只有一个子组件。


connect

正如其名,该方法才是连接react和redux的核心。其意义在于从展示组件派生出一个结合redux的容器组件,我们不用关心状态管理的细节,只用专注于展示组件的逻辑即可。先来看一下connect之后返回的组件结构:

export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {
    // 参数处理
    // ...
    return function wrapWithConnect(WrappedComponent) {

        class Connect extends Component {
            constructor(props, context) {
                super(props, context)
                this.store = props.store || context.store;
                const storeState = this.store.getState()
                this.state = { storeState }
            }
            // 周期方法及操作方法
            // ...
            render(){
          // ...
                this.renderedElement = createElement(WrappedComponent,
                    this.mergedProps 
                )
                return this.renderedElement;
            }
        }
        return hoistStatics(Connect, WrappedComponent);
    }
}

可以看到首先在构造时就获取到了Provider提供的store,然后根据store获取到所有state,渲染时将mapStateToProps,mapDispatchToProps,props合并后传给展示组件, 并通过hoistStatics方法将展示组件的静态方法复制进connect组件,即完成了结合流程。下面再分析每个参数的意义。

mapStateToProps

该参数必须是一个函数,该函数也有两个参数,state表示store中的所有state,props表示通过组件connect传入的props,返回值即是merge进props的state。通过该参数将组件和Redux的状态树生成联系,计算待merge的state的源码如下:

computeStateProps(store, props) {
    if (!this.finalMapStateToProps) {
        return this.configureFinalMapState(store, props);
    }
  const state = store.getState();
  const stateProps = this.doStatePropsDependOnOwnProps ?
        this.finalMapStateToProps(state, props) :
          this.finalMapStateToProps(state)
  if (process.env.NODE_ENV !== 'production') {
      checkStateShape(stateProps, 'mapStateToProps');
  }
  return stateProps;
}

其中作为参数的state通过store.getState()获取

mapDispatchToProps

该参数可以是actionCreators函数或者对象。当参数是对象时会运行redux中的bindActionCreators方法将其转化为函数形式。源码如下:

export default function wrapActionCreators(actionCreators) {
        return dispatch => bindActionCreators(actionCreators, dispatch)
}

function bindActionCreator(actionCreator, dispatch) {
  return (...args) => dispatch(actionCreator(...args))
}
export default function bindActionCreators(actionCreators, dispatch) {
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }
  if (typeof actionCreators !== 'object' || actionCreators === null) {
    throw new Error(
      `bindActionCreators expected an object or a function, instead received ${actionCreators === null ? 'null' : typeof actionCreators}. ` +
      `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
    )
  }
  var keys = Object.keys(actionCreators)
  var boundActionCreators = {}
  for (var i = 0; i < keys.length; i++) {
    var key = keys[i]
    var actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}

转化为函数后的映射就和mapStateToProps相似了,返回值即是merge进props的Dispatch方法。其中作为参数的dispatch就是store.dispatch。

mergeProps

该参数是一个定义了mapState,mapDispatch以及this.props合并规则的函数,默认规则如下:

const defaultMergeProps = (stateProps, dispatchProps, parentProps) => ({
    ...parentProps,
    ...stateProps,
      ...dispatchProps
})

如果三个对象中字段有同名则后者会覆盖前者。

options

该参数是一个有pure和withRef两个属性的对象,pure表示是否开启pure优化,默认为true。withRef用来给展示组件注册一个ref,默认为false,开启时可以通过getWrappedInstance方法获取这个ref。


响应式机制

通过props中的dispatch函数可以派发action更新store,store的变化触发重新渲染的机制需要通过store的subscribe函数来实现。在connect组件中订阅变化的源码如下:

componentDidMount() {
    this.trySubscribe();
}
trySubscribe() {
    if (shouldSubscribe && !this.unsubscribe) {
        this.unsubscribe = this.store.subscribe(this.handleChange.bind(this));
      this.handleChange();
    }
}
componentWillUnmount() {
    this.tryUnsubscribe();
    this.clearCache();
}

可以看到在生命周期的挂载和卸载时,分别注册和注销了监听store变化的handleChange函数。接下来看处理变化的逻辑:

if (!this.unsubscribe) {
    return;
}
const storeState = this.store.getState();
const prevStoreState = this.state.storeState;
if (pure && prevStoreState === storeState) {
    return;
}
if (pure && !this.doStatePropsDependOnOwnProps) {
    const haveStatePropsChanged = tryCatch(this.updateStatePropsIfNeeded, this);
  if (!haveStatePropsChanged) {
      return;
  }
  if (haveStatePropsChanged === errorObject) {
      this.statePropsPrecalculationError = errorObject.value;
  }
  this.haveStatePropsBeenPrecalculated = true;
}
this.hasStoreStateChanged = true;
this.setState({storeState})

通过在connect组件中对比state的前后变化来决定是否调用setState触发重渲染,pure参数则用来决定是否只检测map相关的state数据变化。

-- EOF --

添加在分类「 前端开发 」下,并被添加 「React」 标签。