说明:此篇作为ARTS学习打卡专用(何为ARTS,详见这里),每周输出一篇原创能力有限,但是自己不想说放弃这个 flag,所以以下内容可能存在(但不限于)如下问题: 1. 篇幅短小。 2. 解释不够详尽。 3. 内容不够严谨。 4. 措辞未经修饰。
目的
学习下 redux 如何通过 store 来驱动更新 react 组件,及其优化方式。以下是基于react-redux4.4.10。
React-Redux 实现思路
如何让组件知道 state 更新
基本思路是通过把 store 注入到 context 里面让所有组件都有机会通过 context 获取到 store,并订阅 state 的变化。我们看下 Provider 实现:
class Provider extends React.Component {
getChildContext() {
return {
store: this.store
}
}
constructor( props ) {
super( props );
this.store = props.store
}
render() {
return Children.only( this.props.children );
}
}
然后通过 connect 给组件外层包一层 hoc,在这个 hoc 中订阅 state 的变化,示例代码:
function connect( mapStateToProps, mapDispatchToProps, mergeProps, options={} ) {
return function wrapWithConnect( WrappedComponent ) { // WrappedComponent 需要包装的组件
class Connect extends React.Component {
constructor( props, context ) {
super( props, context );
this.store = context.store;
this.state = {
storeState: this.store.getState()
}
}
componentDidMount() {
let store = this.props.store;
this.store.subcribe( ()=>{
let newStoreState = store.getState();
this.setState( function(){
return {
storeState: newStoreState
}
} )
} )
}
render() {
let storeState = this.state.storeState;
let props = this.props;
let stateProps = mapStateToProps( storeState, props );
let dispatchProps = mapDispatchToProps( this.store.dispatch, props );
let mergedProps = mergeProps( stateProps, dispatchProps, props );
return <WrappedComponent {...mergedProps}/>
}
}
return Connect;
}
}
如何避免不不必要的更新
上面的代码会导致一个问题,就是每次只要 store 保存的 state 更新,那么所有订阅的组件都要去更新,无论组件关系的状态是否有变化,那么我们就需要在触发组件更新前做下比较
function connect( mapStateToProps, mapDispatchToProps, mergeProps, options={} ) {
return function wrapWithConnect( WrappedComponent ) { // WrappedComponent 需要包装的组件
class Connect extends React.Component {
constructor( props, context ) {
super( props, context );
this.store = context.store;
this.state = {
storeState: this.store.getState()
}
}
componentDidMount() {
let store = this.props.store;
this.store.subcribe( ()=>{
let newStoreState = store.getState();
// 1.
// 比较 mapStateToProps 接收一个参数时候 返回的 props 有没有变化
// 如果 mapStateToProps 接收的两个参数,那么还要看 props 有没有变化
let newStateProps = mapStateToProps( newStoreState );
if ( this.stateProps && shallowEqual(this.stateProps, newStateProps ) ) {
return
}
this.stateProps = newStateProps;
this.hasStoreStateChanged = true
this.setState( function(){
return {
storeState: newStoreState
}
} )
} )
}
componentWillReceiveProps(nextProps) {
// 2.
// 比较下 props 有没有变化
if (!shallowEqual(nextProps, this.props)) {
this.haveOwnPropsChanged = true
}
}
shouldComponentUpdate() {
// 3.
// 如果 1 和 2 中任何有变化,都需要更新组件
return this.haveOwnPropsChanged || this.hasStoreStateChanged
}
render() {
let storeState = this.state.storeState;
let props = this.props;
let stateProps = mapStateToProps( storeState, props );
let dispatchProps = mapDispatchToProps( this.store.dispatch, props );
let mergedProps = mergeProps( stateProps, dispatchProps, props );
return <WrappedComponent {...mergedProps}/>
}
}
return Connect;
}
}
最后即使 stateProps 有变化, props 有变化,如果最终 mergedProps 没有变化,那么 render 最后 return 出去的 组件也可以做缓存。所以源码里面在 render 里面还对最终 mergedProps 做了比较。这里就不详述了,因为思路已经带到。