zoukankan      html  css  js  c++  java
  • React-redux原理探索

    先看一段react-redux代码再探索原理

    import React from 'react';
    import { render } from 'react-dom';
    import { createStore } from 'redux';
    import { Provider } from 'react-redux';
    import routes from './router';
    import reducer from './reducer';
    
    const store = createStore(
      reducer
    );
    
    render(
      <Provider store={store}>
        {routes}
      </Provider>,
      document.getElementById('root')
    );

    先调用redux的createStore生成store,再将store作为属性传给Provider组件。

    现在看看react-redux源代码。

    index.js页暴露了以下几个接口

    export { Provider, createProvider, connectAdvanced, connect }

    上面的demo使用的是Provider组件,Provider的主要作用是使得其所有子组件可以通过context访问到Redux的store,现在深入了解一下Provider组件的实现原理。

      class Provider extends Component {
        // 将store放入context使子孙组件能够访问到store
        getChildContext() {
          return { [storeKey]: this[storeKey], [subscriptionKey]: null }
        }
    
        constructor(props, context) {
          super(props, context)
          // 获取redux实例
          this[storeKey] = props.store;
        }
    
        render() {
          // 使整个应用成为Provider的子组件
          // 确保Provider组件的直接子级为单个封闭元素,切勿多个组件平行放置。
          return Children.only(this.props.children)
        }
      }
      // Redux 2.x 与React-Redux 2.x不再支持热重载的reducer,所以在非生产环境下,
      // 我们会为Provider添加生命周期函数componentWillReceiveProps,如果store的值发生了变化,就会在提供警告提示
      if (process.env.NODE_ENV !== 'production') {
        Provider.prototype.componentWillReceiveProps = function (nextProps) {
          if (this[storeKey] !== nextProps.store) {
            warnAboutReceivingStore()
          }
        }
      }
    
      Provider.propTypes = {
        store: storeShape.isRequired,
        children: PropTypes.element.isRequired,
      }
      Provider.childContextTypes = {
        [storeKey]: storeShape.isRequired,
        [subscriptionKey]: subscriptionShape,
      }
    
      return Provider
    }

    connect的作用就是将state和dispatch绑定到react组件中,使得组件可以访问到redux,下面为connect使用demo

    export default connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {})(ReactComponent)

    connect源代码比较长,把connect的核心实现简化提取出来是下面的形式,最终返回的是把state和dispatch绑定到Connect的组件。

    funtion connect(mapStateToProps,mapDispatchToProps,mergeProps,{一堆props}) {
    return function wrapWithConnect(WrappedComponent) {
            class Connect extends Component {
            
            }
            return hoistStatics(Connect, WrappedComponent)
        }
    }

    其中hoistStatics(Connect, WrappedComponent)是自动把所有绑定在WrappedComponent对象上的非React方法都绑定到Connect上。

    先看看传入的四个参数mapStateToProps,mapDispatchToProps,mergeProps,{一堆props}作用

        const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, 'mapStateToProps')
        const initMapDispatchToProps = match(mapDispatchToProps, mapDispatchToPropsFactories, 'mapDispatchToProps')
        const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')
    
        return connectHOC(selectorFactory, {
          // used in error messages
          methodName: 'connect',
    
           // used to compute Connect's displayName from the wrapped component's displayName.
          getDisplayName: name => `Connect(${name})`,
    
          // if mapStateToProps is falsy, the Connect component doesn't subscribe to store state changes
          shouldHandleStateChanges: Boolean(mapStateToProps),
    
          // passed through to selectorFactory
          initMapStateToProps,
          initMapDispatchToProps,
          initMergeProps,
          pure,
          areStatesEqual,
          areOwnPropsEqual,
          areStatePropsEqual,
          areMergedPropsEqual,
    
          // any extra options args can override defaults of connect or connectAdvanced
          ...extraOptions
        })

    通过match方法生成新的函数传递给connectHOC(生成Connect组件的方法)

    initMapStateToProps分两种情况,不传或者传参为null时返回一个函数(运行传参state返回空对象),传参为函数时返回一个函数(执行后返回state中所传入的属性,例如下边的函数会返回{todo:[]})
    const mapStateToProps = state => ({
      todos: []
    })
    initMapDispatchToProps分为三种:
    • 不传参时返回函数(函数运行参数传入dispatch,返回对象{dispatch:function dispatch(){xxx}});
    • 传参为函数时返回函数(函数运行传参dispatch返回对象,例如下边函数会返回{onClick:function(){xxx}})
    const mapDispatchToProps = (dispatch, ownProps) => ({
      onClick: () => dispatch(setVisibilityFilter(ownProps.filter))
    })
    • 传参为对象时返回函数(函数运行传参dispatch,返回{xxx:function(){xxx}})

    initMergeProps不传参默认返回 { ...ownProps, ...stateProps, ...dispatchProps }将所有props全部合成一个对象

    这三种参数全部初始化完毕后执行connectHOC生成Connect组件,Connect组件会初始化两样东西

    • initSelector:selector的主要作用是通过执行initMapStateToProps,initMapDispatchToProps,initMergeProps生成的函数计算新的props,并返回纯对象(plain object),将这个对象作为props传递给被包裹的组件(WrappedComponent)
          initSelector() {
            // 首先调用selectorFactory从而初始化sourceSelector,我们并不会直接调用sourceSelector,而是为了程序的健壮,
            // 通过将sourceSelector作为参数调用makeSelectorStateful,返回更加安全的selector。
            // 从此之后,我们想要生成新的props只需要调用selector.run函数。
            // 在selector.run函数中对sourceSelector的异常做了处理,并用sourceSelector.error记录是否存在异常
            // function pureFinalPropsSelector(nextState, nextOwnProps) {
            //   return hasRunAtLeastOnce
            //     ? handleSubsequentCalls(nextState, nextOwnProps)
            //     : handleFirstCall(nextState, nextOwnProps)
            // }
            const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions)
            this.selector = makeSelectorStateful(sourceSelector, this.store)
            this.selector.run(this.props)
          }

    通过执行selector.run设置selector.shouldComponentUpdate再通过shouldComponentUpdate判断selector.shouldComponentUpdate通知组件是否刷新

      const selector = {
        run: function runComponentSelector(props) {
          try {
            const nextProps = sourceSelector(store.getState(), props)
            if (nextProps !== selector.props || selector.error) {
              selector.shouldComponentUpdate = true
              selector.props = nextProps
              selector.error = null
            }
          } catch (error) {
            selector.shouldComponentUpdate = true
            selector.error = error
          }
        }
      }
    
      shouldComponentUpdate() {
        return this.selector.shouldComponentUpdate
      }
    • initSubscription为store增加监听事件,在store数据变化时执行onStateChange函数
          initSubscription() {
            if (!shouldHandleStateChanges) return
    
            // parentSub's source should match where store came from: props vs. context. A component
            // connected to the store via props shouldn't use subscription from context, or vice versa.
            const parentSub = (this.propsMode ? this.props : this.context)[subscriptionKey]
            this.subscription = new Subscription(this.store, parentSub, this.onStateChange.bind(this))
    
            this.notifyNestedSubs = this.subscription.notifyNestedSubs.bind(this.subscription)
          }

    onStateChange函数是store发生改变的回调函数,当回调onStateChange方法时,会通过selector计算新的props,如果计算selcetor的结果中shouldComponentUpdatefalse,表示不需要刷新当前组件仅需要通知子组件更新。如果shouldComponentUpdatetrue,会通过设置this.setState({})来刷新组件,并使得在组件更新结束之后,通知子组件更新。

          onStateChange() {
            this.selector.run(this.props)
    
            if (!this.selector.shouldComponentUpdate) {
              this.notifyNestedSubs()
            } else {
              this.componentDidUpdate = this.notifyNestedSubsOnComponentDidUpdate
              this.setState(dummyState)
            }
          }

    在render操作时将selector生成的新props作为参数传到WrappedComponent组件中,WrappedComponent组件就是connect()(ReactComponent)中传入的ReactComponent

          addExtraProps(props) {
            if (!withRef && !renderCountProp && !(this.propsMode && this.subscription)) return props
            const withExtras = { ...props }
            if (withRef) withExtras.ref = this.setWrappedInstance
            if (renderCountProp) withExtras[renderCountProp] = this.renderCount++
            if (this.propsMode && this.subscription) withExtras[subscriptionKey] = this.subscription
            return withExtras
          }
    
          render() {
            const selector = this.selector
            selector.shouldComponentUpdate = false
    
            if (selector.error) {
              throw selector.error
            } else {
              return createElement(WrappedComponent, this.addExtraProps(selector.props))
            }
          }

    将selector.props中的属性传到WrappedComponent,这个props是经过计算的所有props的合集。

    参考:

    https://segmentfault.com/a/1190000010188279

    https://segmentfault.com/a/1190000010113286

    https://segmentfault.com/a/1190000010345345

  • 相关阅读:
    Oracle常用系统查询SQL
    easyui中使用的遮罩层
    EasyUI相同的Tab只打开一个(即EasyUI方法的调用方法)
    jQueryEasyUI创建菜单主页
    linux 的环境变量的配置文件
    angular reactive form
    svn代码回滚
    golang restful api
    golang embedded structs
    Angular Multiple HTTP Requests with RxJS
  • 原文地址:https://www.cnblogs.com/xcsun/p/9146342.html
Copyright © 2011-2022 走看看