原版react-redux解决了原版redux调用dispatch后需要注册subscribe去手动触发render或者this.setState更新组件的问题
原版react-redux最核心的就是Provider和connect
- Provider
Provider是一个高阶组件通常情况下将最顶层的祖宗组件包裹起来,使得所有的子孙组件能够接收store这个全局对象,这里需要用到react中的一个api Context
context就承载了provider的主要功能
和原版react-redux一样,在外部定义store,然后通过store属性将store作为provider的props传入provider内部
cnpm install prop-types --save
通过在祖宗组件中申明静态变量来确定子组件可以通过context属性拿到哪些属性,这里必须使用prop-types这个插件来约束自组件将要拿到的数据类型,(不规定的话,拿到的context是空对象{}),同时还需要用到getChildContext()这个方法,return的内容就是子孙组件能拿到的context
static childContextTypes = {
storeState: PropTypes.object,
};
getChildContext() {
return this.state;
}
Provider的实现思路就是监听外部传入的store的变化,如果变化则执行this.setState更新Provider的state来触发组件刷新
componentDidMount() {
const {store} = this.props;
store.subscribe((newState) => {
this.setState({
storeState: newState
});
});
}
最核心的部分就是在componentDidMount这个时候,从props将store拿出来,然后调用store.subscribe添加执行事件,在这里我们通过回调拿到最新的state,并setState就可以了
深坑!!!:由于store更新引起的生命周期方法只有一次,也就是说,我们在ReactDom.render组件时,向provider传入的store属性,只会在初始化时传入一次,其他任何时机的修改,不会引起react内部的render或其他生命周期的调用,所以你无法在Provider内部使用任何生命周期监听到store的变化,只能通过dispatch后的subscribe来更新组件
Provider的render部分核心很简单,直接返回this.props.children即可,这是reacct内部的属性,不会影响被provider包裹的组件
- Connect
provider终究只是个“提供者”,他提供全局对象,但是一个组件要拿到他提供的全局对象,就必须要用到另一个重要的核心–Connect
Connect是一个高阶函数,接收一个react组件,返回一个props中含有store的组件,起连接作用
仿造原版Connect,这里暂时只做了前两个参数的回调,mapStateToProps, mapDispatchToProps分别为两个函数,传入的回调分别为store.state和dtore.dispatch,在组件connect时可以自定义想从props中获取的数据,或者通过dispatch的自定义函数
这样app中包含了从store.state中解构出的payload和a属性,第二个参数为组件本身的props
在实现connect时需要注意的是,构造函数上从父类继承的props就是ownProps,并且在componentWillReceiveProps中从nextContext中获取最新的store.state,然后调用this.state触发页面render刷新
constructor(props, context) {
super(props, context);
const {state, ...storeFunc} = context.store;
this.state = {
myState: state,
ownProps: props,
storeFunc: storeFunc,
};
}
componentWillReceiveProps(nextProps, nextContext) {
this.setState({
myState: nextContext.storeState,
ownProps: nextProps
});
}
码云地址:https://gitee.com/onezilc/MyRedux.git