react-redux 学习笔记

Provider

作用:将 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)
  }
}

Provider 包裹在组件的最外层,接收一个 store tree,首先在 constructor 里把接收到的属性存到 this.store 里,在 getChildContext 里把 store return 出来,这样包在它里面的组件都可以通过 context 取到 store,将 store 传给了所有的子组件。render 中 Children.only 表示只 render 其中第一个子组件,并且要求组件的第一级子组件只有一个。

connect

作用:在每一次 state 变化之后,用新的 store tree 重新渲染组件。
connect 函数接收 4 个参数:

mapStateToProps

参数为整个 store tree,返回值为需要 merge 进 props 的 state。

mapDispatchToProps

参数为整个 store.dispatch(),返回值为需要 merge 进 props 的 action。

mergeProps

将前面两个函数的返回值,加上自定义的属性,合并到一起,挂到容器组件的 this props 上。
**注意:**如果写了第三个参数,前两个参数返回的值将不会直接被挂到 this.props 上,所以第三个参数需要返回正确的值。

options

是否开启优化,默认值为 true。

connect 在容器组件中的用法:

function mapStateToProps(state) {
	const { isFetching, objectTypes } = state.rootData.objectGroup || {
		isFetching: true,
		objectTypes: []
	}
	return { isFetching, objectTypes }
}

function mapDispatchToProps(dispatch) {
  const actionCreators = Object.assign({}, objectsTypeActionCreators);
	return bindActionCreators(actionCreators, dispatch);
}

export default connect(mapStateToProps, mapDispatchToProps)(CRMPage);

从 connect 的用法可以看出,这里 connect 接收了两个参数,分别处理 state 和 action,最终返回一个组件。最终 export 出去的不是最初定义的 React 组件,是用 connect 包裹之后的组件。所以 connect 做的事主要有:

  1. 返回一个 React 组件,将函数传入的参数(state,action)放入 this.props 下。
  2. 在组件结构加载完成后注册监听器,在组件卸载时注销监听器。监听器的作用是捕捉每一次 state 的更新。

具体实现是

  1. 在 constructor 取得当前的 store 树,可以通过 this.props.store 和 this.context.store 两种方式获取,其中第二种方式是从 Provider 的 getChildContext() 传进来的。

  2. 在 render 里使用 React 的 createElement方法生成新的组件,并将传入的 state 和 action 合并到 props 上。

render(){
  this.renderedElement = createElement(WrappedComponent,
    this.mergedProps //merge stateProps, dispatchProps, props
  )
  return this.renderedElement;
 }

最后返回的时候用hoistNonReactStatic将原来组件中的元素拷贝到目标组件。

hoistNonReactStatic(targetComponent, sourceComponent);
  1. 在 componentDidMount 中使用 store 的 subscribe 方法注册监听事件,以便在 dispatch 之后调用,componentWillUnmount 执行 unsubcribe 事件,注销监听器。
trySubscribe() {
  if (shouldSubscribe && !this.unsubscribe) {
    this.unsubscribe = this.store.subscribe(this.handleChange.bind(this))
    this.handleChange()
  }
}

tryUnsubscribe() {
  if (this.unsubscribe) {
    this.unsubscribe()
    this.unsubscribe = null
  }
}

这样,在每次 dispatch(action) 之后会执行 handleChange,在 handleChange 函数内部,会执行 setState 更新 store tree。

所以每次 dispatch(action) 之后,执行对应的 reducer,然后执行相应的订阅的函数。在 reducer 里把 store 更新,在监听函数里执行 setState,更新 React 组件。