Redux系列源码解读

Redux 源码解读

1.redux-action createAction

redux-action的createAction方法就是给我们提供了灵活的创建符合FSA标准的action的方法

exports.default = createAction;
 
/**
  返回创建action的函数
*/
function createAction(type,payloadCreator,MetaCreator) {
  var finalPayloadCreator = typeof payloadCreator === 'function' ? payloadCreator : _identity2.default;
  /**
    返回的函数
  */
  var actionHandler = function actionHandler() {
    var hasError = (arguments.length <= 0 ? undefined : arguments[0]) instanceof Error;
  /**
    返回的action
  */
    var action = {
      type: type
    };
    //根据传入的参数,执行payloadCreator获取payload
    var payload = hasError ? arguments.length <= 0 ? undefined : arguments[0] : finalPayloadCreator.apply(undefined,arguments);
    if (!(payload === null || payload === undefined)) {
      action.payload = payload;
    }
 
    if (hasError) {
      // Handle FSA errors where the payload is an Error object. Set error.
      action.error = true;
    }
  //根据传入的参数,执行MetaCreator获取payload
    if (typeof MetaCreator === 'function') {
      action.Meta = MetaCreator.apply(undefined,arguments);
    }
    //可以看到  payloadCreator和MetaCreator的参数都是用的传给actionHandler的参数
 
    return action;
  };
 
  actionHandler.toString = function () {
    return type.toString();
  };
 
  return actionHandler;
}

2.redux combineReducer

redux的combineReducer方法 用于将多个reducer,合并成一个大的reducer函数,传给store。

用于分解state树,每一个reducer对应state的一个key对应的子state。比如poi的reducer对应的就是state[poi]。这样在将state传递给props时利于分解。

reducer以对象的形式传入,finalReducers 存放最终的reducer,finalReducerKeys存放reducer的key
最终返回  combination函数 reducer类型的函数,接受state和action 返回state
 
state的形式是一个大对象下面每一个reducer对应一个子state。
 
触发一个action,会遍历所有的reducer,
将该reducer的旧state和action传入,然后根据返回的新的state对象是否改变,来决定
最终的返回的state是否改变。
这里需要注意:由于state都是引用类型,这里比较是值比较
hasChanged = hasChanged || nextStateForKey !== prevIoUsstateForKey
 
所以如果我们想要改变全局的state,需要在reducer中返回新的对象,而不是原来的state对象,
如果返回原来的对象,即使对象里的值改变了,也不会引起全局state的改变。
 */
export default function combineReducers(reducers) {
  var reducerKeys = Object.keys(reducers)
  var finalReducers = {}
  for (var i = 0; i < reducerKeys.length; i++) {
    var key = reducerKeys[i]
 
    if (process.env.NODE_ENV !== 'production') {
      if (typeof reducers[key] === 'undefined') {
        warning(`No reducer provided for key "${key}"`)
      }
    }
 
    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  var finalReducerKeys = Object.keys(finalReducers)
 
  return function combination(state = {},action) {
    /**
     校验语法错误,reducer返回的state不能是undefined
    */
    if (sanityError) {
      throw sanityError
    }
 
    var hasChanged = false
    var nextState = {}
    for (var i = 0; i < finalReducerKeys.length; i++) {
      var key = finalReducerKeys[i]
      var reducer = finalReducers[key]
      var prevIoUsstateForKey = state[key]
      var nextStateForKey = reducer(prevIoUsstateForKey,action)
      if (typeof nextStateForKey === 'undefined') {
        var errorMessage = getUndefinedStateErrorMessage(key,action)
        throw new Error(errorMessage)
      }
      nextState[key] = nextStateForKey
      /**
        所以如果我们想要改变全局的state,需要在reducer中返回新的对象,而不是原来的state对象,
        如果返回原来的对象,即使对象里的值改变了,也不会引起全局state的改变。
      */
      hasChanged = hasChanged || nextStateForKey !== prevIoUsstateForKey
    }
    return hasChanged ? nextState : state
  }
}

3 redux applyMiddleware

应用中间件的目的是包装dispatch,在action传递给dispatch执行之前,需要经过中间件的层层处理,进行一些业务上的处理,决定action的走向。

源码实现了这种将函数数组,通过reducerRight的方法,实现层层嵌套的执行,达到中间件的实现。

/**
  显示执行中间件,得到中间件的返回函数数组chain,然后利用compose方法生成嵌套的执行chain
  方法的包装dispatch函数,
  中间件的形式是
  (getState,dispatch)=> next => action => {
     next(action);
  }
 */
export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer,preloadedState,enhancer) => {
    var store = createStore(reducer,enhancer)
    var dispatch = store.dispatch
    var chain = []
 
    var middlewareAPI = {
      getState: store.getState,dispatch: (action) => dispatch(action)
    }
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)
    /**
    store.dispatch 就是第一个next  是last ware的next
    (...args) => {
      return ware0(ware1(ware2(last(...args))))
    }
    dispatch = ware0(ware1(ware2(last(...args))))
    所以中间件中next传入后返回的函数就是我们需要的函数形式,
    例如dispatch 需要的函数形式是 传一个action
    */
    return {
      ...store,dispatch
    }
  }
}
 
/**
reduceRight是数组的从右至左执行,
初始的参数是最后一个函数接受dispatch,
的到的一个action=>{
    dispatch(action);
}
形式的函数,作为参数composed
f的形式是
next=>action=>{
}
最终形成的就是
(...args) => {
    return funcs0(funcs1(funcs2(last(...args))))
}
*/
export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }
 
  if (funcs.length === 1) {
    return funcs[0]
  }
 
  const last = funcs[funcs.length - 1]
  const rest = funcs.slice(0,-1)
  return (...args) => rest.reduceRight((composed,f) => f(composed),last(...args))
}

中间件执行过程模拟

中间件原理
*/
function func1 (next) {
    console.log('func1 return');
    return function (action) {
        console.log('func1start');
        next(action);
        console.log('func1end');
    }
     
}
function func2 (next) {
    console.log('func2 return');
    return function (action) {
        console.log('func2start');
        next(action);
        console.log('func2end');
    }
}
function func3 (next) {
    console.log('func3 return');
    return function (action) {
        console.log('func3start');
        next(action);
        console.log('func3end');
    }
}
 
function dispatch(action) {
    console.log(action);
}
 
function afterCompose (args) {
    return func1(func2(func3(args)));
}
 
var newdispatch = afterCompose(dispatch);
newdispatch('action');
/**
    执行顺序
    func3 return
    func2 return
    func1 return
    func1start
    func2start
    func3start
    action
    func3end
    func2end
    func1end
*/

4 redux createStore

应用场景参见中间件的应用代码与applyMiddleware源码,是redux提供创建store的方法

import isPlainObject from 'lodash/isPlainObject'
import $$observable from 'symbol-observable'
 
export var ActionTypes = {
  INIT: '@@redux/INIT'
}
 
export default function createStore(reducer,enhancer) {
  var currentReducer = reducer
  var currentState = preloadedState
  var currentListeners = []
  var nextListeners = currentListeners
  var isdispatching = false
 
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }
 
  function getState() {
    return currentState
  }
 
   /**
    订阅监听
   */
  function subscribe(listener) {
    if (typeof listener !== 'function') {
      throw new Error('Expected listener to be a function.')
    }
 
    var isSubscribed = true
 
    ensureCanMutateNextListeners()
    nextListeners.push(listener)
 
    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }
 
      isSubscribed = false
 
      ensureCanMutateNextListeners()
      var index = nextListeners.indexOf(listener)
      nextListeners.splice(index,1)
    }
  }
 
   /**
    执行reducer,获取state,执行listener
   */
  function dispatch(action) {
    try {
      isdispatching = true
      currentState = currentReducer(currentState,action)
    } finally {
      isdispatching = false
    }
 
    var listeners = currentListeners = nextListeners
    for (var i = 0; i < listeners.length; i++) {
      listeners[i]()
    }
    return action
  }
 
   /**
    替换reducer
   */
  function replaceReducer(nextReducer) {
    if (typeof nextReducer !== 'function') {
      throw new Error('Expected the nextReducer to be a function.')
    }
    currentReducer = nextReducer
    dispatch({ type: ActionTypes.INIT })
  }
 
 
  /**
    store创建的时候,获取初始的sate树
  */
  dispatch({ type: ActionTypes.INIT })
 
  return {
    dispatch,subscribe,getState,replaceReducer
  }
}

5. react-redux Provider

redux和react的结合,Provider作为根组件,将store的state放在context中供子组件使用。

import { Component,PropTypes,Children } from 'react'
import storeShape from '../utils/storeShape'
import warning from '../utils/warning'
 
 
export default class Provider extends Component {
  //把 store 放在context里面,给子元素用
  getChildContext() {
    return { store: this.store }
  }
 
  constructor(props,context) {
    super(props,context)
    this.store = props.store
  }
 
  render() {
    const { children } = this.props
    //渲染唯一的子元素
    return Children.only(children)
  }
}
 
Provider.propTypes = {
  store: storeShape.isrequired,children: PropTypes.element.isrequired
}
Provider.childContextTypes = {
  store: storeShape.isrequired
}

6. react-redux connect

connect方法,将React的组件进行包装,包装的目的如下:

  • 能够将store中指定的state,传递给组件当props

  • 能够监听store中state的变化

  • 能够将action传递给view

const defaultMapStatetoProps = state => ({}) // eslint-disable-line no-unused-vars
const defaultMapdispatchToProps = dispatch => ({ dispatch })
const defaultMergeProps = (stateProps,dispatchProps,parentProps) => ({
  ...parentProps,...stateProps,...dispatchProps
})
 
 
export default function connect(mapStatetoProps,mapdispatchToProps,mergeProps,options = {}) {
 
  //返回包装组件的函数
  return function wrapWithConnect(WrappedComponent) {
 
    class Connect extends Component {
      shouldComponentUpdate() {
        return !pure || this.haveOwnPropsChanged || this.hasstoreStateChanged
      }
 
      constructor(props,context) {
        super(props,context)
        this.version = version
        this.store = props.store || context.store
 
       
        const storeState = this.store.getState()
        this.state = { storeState }
        this.clearCache()
      }
 
      isSubscribed() {
        return typeof this.unsubscribe === 'function'
      }
 
      trySubscribe() {
        if (shouldSubscribe && !this.unsubscribe) {
          //订阅store的state变化
          this.unsubscribe = this.store.subscribe(this.handleChange.bind(this))
          this.handleChange()
        }
      }
 
      tryUnsubscribe() {
        if (this.unsubscribe) {
          this.unsubscribe()
          this.unsubscribe = null
        }
      }
 
      componentDidMount() {
        //订阅store的state变化
        this.trySubscribe()
      }
 
      componentwillReceiveProps(nextProps) {
        if (!pure || !shallowEqual(nextProps,this.props)) {
          this.haveOwnPropsChanged = true
        }
      }
 
      componentwillUnmount() {
        this.tryUnsubscribe()
        this.clearCache()
      }
 
      //订阅变化
      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
        //如果有变化 setState,触发render
        this.setState({ storeState })
      }
 
      render() {
        const {
          haveOwnPropsChanged,hasstoreStateChanged,haveStatePropsBeenPrecalculated,statePropsPrecalculationError,renderedElement
        } = this
 
        
        //最终渲染组件,将合并属性传递给WrappedComponent
        if (withRef) {
          this.renderedElement = createElement(WrappedComponent,{
            ...this.mergedProps,ref: 'wrappedInstance'
          })
        } else {
          this.renderedElement = createElement(WrappedComponent,this.mergedProps
          )
        }
 
        return this.renderedElement
      }
    }
 
    Connect.displayName = connectdisplayName
    Connect.WrappedComponent = WrappedComponent
    Connect.contextTypes = {
      store: storeShape
    }
    Connect.propTypes = {
      store: storeShape
    }
 
    //把WrappedComponent的非静态react属性 复制到Connect,最终返回Connect
    return hoistStatics(Connect,WrappedComponent)
  }
}

7. redux bindActionCreators

使用实例参见 react-redux connect 的使用实例

function bindActionCreator(actionCreator,dispatch) {
  return (...args) => dispatch(actionCreator(...args))
}
 
 
  将actionCreators绑定上dispatch,key还是actionCreators的key,但是多做了一层dispatch
 */
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
}

相关文章

react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接...
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc ...