Redux入坑进阶之源码解析

开发 前端
redux 函数内部包含了大量柯里化函数以及代码组合思想。通俗的来讲,可以用一句话概括柯里化函数:返回函数的函数。代码组合就像是数学中的结合律,通过这样函数之间的组合,可以大大增加可读性,效果远大于嵌套一大堆的函数调用,并且我们可以随意更改函数的调用顺序。

[[171968]]

预热

redux 函数内部包含了大量柯里化函数以及代码组合思想

柯里化函数(curry)

通俗的来讲,可以用一句话概括柯里化函数:返回函数的函数

// example 
const funcA = (a) => { 
  return const funcB = (b) => { 
    return a + b 
  } 
};  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

上述的funcA函数接收一个参数,并返回同样接收一个参数的funcB函数。

柯里化函数有什么好处呢?

  • 避免了给一个函数传入大量的参数--我们可以通过柯里化来构建类似上例的函数嵌套,将参数的代入分离开,更有利于调试
  • 降低耦合度和代码冗余,便于复用

举个栗子:

// 已知listA, listB两个Array,都由int组成,需要筛选出两个Array的交集 
const listA = [1, 2, 3, 4, 5]; 
const listB = [2, 3, 4]; 
 
const checkIfDataExist = (list) => { 
  return (target) => { 
    return list.some(value => value === target) 
  }; 
}; 
// 调用一次checkIfDataExist函数,并将listA作为参数传入,来构建一个新的函数。 
// 而新函数的作用则是:检查传入的参数是否存在于listA里 
const ifDataExist = checkIfDataExist(listA); 
 
// 使用新函数来对listB里的每一个元素进行筛选 
const intersectionList = listB.filter(value => ifDataExist(value)); 
console.log(intersectionList); // [2, 3, 4]  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

代码组合(compose)

代码组合就像是数学中的结合律:

const compose = (f, g) => { 
  return (x) => { 
    return f(g(x)); 
  }; 
}; 
// 还可以再简洁点 
const compose = (f, g) => (x) => f(g(x));  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

通过这样函数之间的组合,可以大大增加可读性,效果远大于嵌套一大堆的函数调用,并且我们可以随意更改函数的调用顺序

Redux

combineReducers 

// 回顾一下combineReducers的使用格式 
 
// 两个reducer 
const todos = (state = INIT.todos, action) => { 
  // .... 
}; 
const filterStatus = (state = INIT.filterStatus, action) => { 
  // ... 
}; 
 
const appReducer = combineReducers({ 
  todos, 
  filterStatus 
});  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

还记得combineReducers的黑魔法吗?即:

  1. 传入的Object参数中,对象的key与value所代表的reducer function同名
  2. 各个reducer function的名称和需要传入该reducer的state参数同名

源码标注解读(省略部分): 

export default function combineReducers(reducers) { 
  // ***次筛选,参数reducers为Object 
  // 筛选掉reducers中不是function的键值对 
  var reducerKeys = Object.keys(reducers); 
  var finalReducers = {} 
  for (var i = 0; i < reducerKeys.length; i++) { 
    var key = reducerKeys[i]; 
    if (typeof reducers[key] === 'function') { 
      finalReducers[key] = reducers[key
    } 
  } 
 
  var finalReducerKeys = Object.keys(finalReducers) 
 
  // 二次筛选,判断reducer中传入的值是否合法(!== undefined) 
  // 获取筛选完之后的所有key 
  var sanityError 
  try { 
    // assertReducerSanity函数用于遍历finalReducers中的reducer,检查传入reducer的state是否合法 
    assertReducerSanity(finalReducers) 
  } catch (e) { 
    sanityError = e 
  } 
   
  // 返回一个function。该方法接收state和action作为参数 
  return function combination(state = {}, action) { 
    // 如果之前的判断reducers中有不法值,则抛出错误 
    if (sanityError) { 
      throw sanityError 
    } 
    // 如果不是production环境则抛出warning 
    if (process.env.NODE_ENV !== 'production') { 
      var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action
      if (warningMessage) { 
        warning(warningMessage) 
      } 
    } 
 
    var hasChanged = false 
    var nextState = {} 
    // 遍历所有的key和reducer,分别将reducer对应的key所代表的state,代入到reducer中进行函数调用 
    for (var i = 0; i < finalReducerKeys.length; i++) { 
      var key = finalReducerKeys[i] 
      var reducer = finalReducers[key
      // 这也就是为什么说combineReducers黑魔法--要求传入的Object参数中,reducer function的名称和要和state同名的原因 
      var previousStateForKey = state[key
      var nextStateForKey = reducer(previousStateForKey, action
      // 如果reducer返回undefined则抛出错误 
      if (typeof nextStateForKey === 'undefined') { 
        var errorMessage = getUndefinedStateErrorMessage(keyaction
        throw new Error(errorMessage) 
      } 
      // 将reducer返回的值填入nextState 
      nextState[key] = nextStateForKey 
      // 如果任一state有更新则hasChanged为true 
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey 
    } 
    return hasChanged ? nextState : state 
  } 

 
// 检查传入reducer的state是否合法 
function assertReducerSanity(reducers) { 
  Object.keys(reducers).forEach(key => { 
    var reducer = reducers[key
    // 遍历全部reducer,并给它传入(undefined, action
    // 当***个参数传入undefined时,则为各个reducer定义的默认参数 
    var initialState = reducer(undefined, { type: ActionTypes.INIT }) 
     
    // ActionTypes.INIT几乎不会被定义,所以会通过switch的default返回reducer的默认参数。如果没有指定默认参数,则返回undefined,抛出错误 
    if (typeof initialState === 'undefined') { 
      throw new Error( 
        `Reducer "${key}" returned undefined during initialization. ` + 
        `If the state passed to the reducer is undefined, you must ` + 
        `explicitly return the initial state. The initial state may ` + 
        `not be undefined.` 
      ) 
    } 
 
    var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.'
    if (typeof reducer(undefined, { type }) === 'undefined') { 
      throw new Error( 
        `Reducer "${key}" returned undefined when probed with a random type. ` + 
        `Don't try to handle ${ActionTypes.INIT} or other actions in "redux/*" ` + 
        `namespace. They are considered private. Instead, you must return the ` + 
        `current state for any unknown actions, unless it is undefined, ` + 
        `in which case you must return the initial state, regardless of the ` + 
        `action type. The initial state may not be undefined.` 
      ) 
    } 
  }) 
 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.

createStore

// 回顾下使用方法 
const store = createStore(reducers, state, enhance);  
  • 1.
  • 2.

源码标注解读(省略部分): 

// 对于未知的action.type,reducer必须返回默认的参数state。这个ActionTypes.INIT就可以用来监测当reducer传入未知type的action时,返回的state是否合法 
export var ActionTypes = { 
  INIT: '@@redux/INIT' 

 
export default function createStore(reducer, initialState, enhancer) { 
  // 检查你的state和enhance参数有没有传反 
  if (typeof initialState === 'function' && typeof enhancer === 'undefined') { 
    enhancer = initialState 
    initialState = undefined 
  } 
  // 如果有传入合法的enhance,则通过enhancer再调用一次createStore 
  if (typeof enhancer !== 'undefined') { 
    if (typeof enhancer !== 'function') { 
      throw new Error('Expected the enhancer to be a function.'
    } 
    return enhancer(createStore)(reducer, initialState) 
  } 
 
  if (typeof reducer !== 'function') { 
    throw new Error('Expected the reducer to be a function.'
  } 
 
  var currentReducer = reducer 
  var currentState = initialState 
  var currentListeners = [] 
  var nextListeners = currentListeners 
  var isDispatching = false // 是否正在分发事件 
 
  function ensureCanMutateNextListeners() { 
    if (nextListeners === currentListeners) { 
      nextListeners = currentListeners.slice() 
    } 
  } 
 
  // 我们在action middleware中经常使用的getState()方法,返回当前state 
  function getState() { 
    return currentState 
  } 
 
  // 注册listener,同时返回一个取消事件注册的方法。当调用store.dispatch的时候调用listener 
  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 
      // 从nextListeners中去除掉当前listener 
      ensureCanMutateNextListeners() 
      var index = nextListeners.indexOf(listener) 
      nextListeners.splice(index, 1) 
    } 
  } 
 
  // dispatch方法接收的action是个对象,而不是方法。 
  // 这个对象实际上就是我们自定义action的返回值,因为dispatch的时候,已经调用过我们的自定义action了,比如 dispatch(addTodo()) 
  function dispatch(action) { 
    if (!isPlainObject(action)) { 
      throw new Error( 
        'Actions must be plain objects. ' + 
        'Use custom middleware for async actions.' 
      ) 
    } 
 
    if (typeof action.type === 'undefined') { 
      throw new Error( 
        'Actions may not have an undefined "type" property. ' + 
        'Have you misspelled a constant?' 
      ) 
    } 
    // 调用dispatch的时候只能一个个调用,通过dispatch判断调用的状态 
    if (isDispatching) { 
      throw new Error('Reducers may not dispatch actions.'
    } 
 
    try { 
      isDispatching = true 
      currentState = currentReducer(currentState, action
    } finally { 
      isDispatching = false 
    } 
    // 遍历调用各个linster 
    var listeners = currentListeners = nextListeners 
    for (var i = 0; i < listeners.length; i++) { 
      listeners[i]() 
    } 
 
    return action 
  } 
  // Replaces the reducer currently used by the store to calculate the state. 
  function replaceReducer(nextReducer) { 
    if (typeof nextReducer !== 'function') { 
      throw new Error('Expected the nextReducer to be a function.'
    } 
 
    currentReducer = nextReducer 
    dispatch({ type: ActionTypes.INIT }) 
  } 
  // 当create store的时候,reducer会接受一个type为ActionTypes.INIT的action,使reducer返回他们默认的state,这样可以快速的形成默认的state的结构 
  dispatch({ type: ActionTypes.INIT }) 
 
  return { 
    dispatch, 
    subscribe, 
    getState, 
    replaceReducer 
  } 
 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.

thunkMiddleware

源码及其简单简直给跪... 

// 返回以 dispatch 和 getState 作为参数的action 
export default function thunkMiddleware({ dispatch, getState }) { 
  return next => action => { 
    if (typeof action === 'function') { 
      return action(dispatch, getState); 
    } 
 
    return next(action); 
  }; 
 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

applyMiddleware

先复习下用法: 

// usage 
import {createStore, applyMiddleware} from 'redux'
import thunkMiddleware from 'redux-thunk'
 
const store = createStore( 
      reducers, 
      state, 
      applyMiddleware(thunkMiddleware) 
);  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

applyMiddleware首先接收thunkMiddleware作为参数,两者组合成为一个新的函数(enhance),之后在createStore内部,因为enhance的存在,将会变成返回enhancer(createStore)(reducer, initialState)

源码标注解读(省略部分): 

// 定义一个代码组合的方法 
// 传入一些function作为参数,返回其链式调用的形态。例如, 
// compose(f, g, h) 最终返回 (...args) => f(g(h(...args))) 
export default function compose(...funcs) { 
  if (funcs.length === 0) { 
    return arg => arg 
  } else { 
    const last = funcs[funcs.length - 1] 
    const rest = funcs.slice(0, -1) 
    return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args)) 
  } 

 
export default function applyMiddleware(...middlewares) { 
  // 最终返回一个以createStore为参数的匿名函数 
  // 这个函数返回另一个以reducer, initialState, enhancer为参数的匿名函数 
  return (createStore) => (reducer, initialState, enhancer) => { 
    var store = createStore(reducer, initialState, enhancer) 
    var dispatch 
    var chain = [] 
 
    var middlewareAPI = { 
      getState: store.getState, 
      dispatch: (action) => dispatch(action
    } 
    // 每个 middleware 都以 middlewareAPI 作为参数进行注入,返回一个新的链。此时的返回值相当于调用 thunkMiddleware 返回的函数: (next) => (action) => {} ,接收一个next作为其参数 
    chain = middlewares.map(middleware => middleware(middlewareAPI)) 
    // 并将链代入进 compose 组成一个函数的调用链 
    // compose(...chain) 返回形如(...args) => f(g(h(...args))),f/g/h都是chain中的函数对象。 
    // 在目前只有 thunkMiddleware 作为 middlewares 参数的情况下,将返回 (next) => (action) => {} 
    // 之后以 store.dispatch 作为参数进行注入 
    dispatch = compose(...chain)(store.dispatch) 
 
    return { 
      ...store, 
      dispatch 
    } 
  } 
 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.

一脸懵逼?没关系,来结合实际使用总结一下:

当我们搭配redux-thunk这个库的时候,在redux配合components时,通常这么写 

import thunkMiddleware from 'redux-thunk'
import { createStore, applyMiddleware, combineReducer } from 'redux'
import * as reducers from './reducers.js'
 
const appReducer = combineReducer(reducers); 
const store = createStore(appReducer, initialState, applyMiddleware(thunkMiddleware));  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

还记得当createStore收到的参数中有enhance时会怎么做吗? 

// createStore.js 
if (typeof enhancer !== 'undefined') { 
  if (typeof enhancer !== 'function') { 
    throw new Error('Expected the enhancer to be a function.'
  } 
  return enhancer(createStore)(reducer, initialState) 
 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

也就是说,会变成下面的情况 

applyMiddleware(thunkMiddleware)(createStore)(reducer, initialState) 
  • 1.
  • applyMiddleware(thunkMiddleware)

applyMiddleware接收thunkMiddleware作为参数,返回形如(createStore) => (reducer, initialState, enhancer) => {}的函数。

  • applyMiddleware(thunkMiddleware)(createStore)

以 createStore 作为参数,调用上一步返回的函数(reducer, initialState, enhancer) => {}

  • applyMiddleware(thunkMiddleware)(createStore)(reducer, initialState)

以(reducer, initialState)为参数进行调用。

在这个函数内部,thunkMiddleware被调用,其作用是监测type是function的action

因此,如果dispatch的action返回的是一个function,则证明是中间件,则将(dispatch, getState)作为参数代入其中,进行action 内部下一步的操作。否则的话,认为只是一个普通的action,将通过next(也就是dispatch)进一步分发。

也就是说,applyMiddleware(thunkMiddleware)作为enhance,最终起了这样的作用:

对dispatch调用的action(例如,dispatch(addNewTodo(todo)))进行检查,如果action在***次调用之后返回的是function,则将(dispatch, getState)作为参数注入到action返回的方法中,否则就正常对action进行分发,这样一来我们的中间件就完成喽~

因此,当action内部需要获取state,或者需要进行异步操作,在操作完成之后进行事件调用分发的话,我们就可以让action 返回一个以(dispatch, getState)为参数的function而不是通常的Object,enhance就会对其进行检测以便正确的处理。

bindActionCreator

这个方法感觉比较少见,我个人也很少用到

在传统写法下,当我们要把 state 和 action 注入到子组件中时,一般会这么做: 

import { connect } from 'react-redux'
import {addTodo, deleteTodo} from './action.js'
 
class TodoComponect extends Component { 
  render() { 
    return ( 
      <ChildComponent  
        deleteTodo={this.props.deleteTodo} 
        addTodo={this.props.addTodo} 
      /> 
    ) 
  } 

 
function mapStateToProps(state) { 
  return { 
    state 
  } 

function mapDispatchToProps(dispatch) { 
  return { 
    deleteTodo: (id) => { 
      dispatch(deleteTodo(id)); 
    }, 
    addTodo: (todo) => { 
      dispatch(addTodo(todo)); 
    } 
  } 

export default connect(mapStateToProps, mapDispatchToProps)(TodoComponect);  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

使用bindActionCreators可以把 action 转为同名 key 的对象,但使用 dispatch 把每个 action 包围起来调用

惟一使用 bindActionCreators 的场景是当你需要把 action creator 往下传到一个组件上,却不想让这个组件觉察到 Redux 的存在,而且不希望把 Redux store 或 dispatch 传给它。 

// 在本组件内的应用 
  addTodo(todo) { 
    let action = TodoActions.addTodo(todo); 
    this.props.dispatch(action); 
  } 
   
  deleteTodo(id) { 
    let action = TodoActions.deleteTodo(id); 
    this.props.dispatch(action); 
  } 
   
  render() { 
    let dispatch = this.props.dispatch; 
    // 传递给子组件 
    let boundActionCreators = bindActionCreators(TodoActions, dispatch); 
    return ( 
      <ChildComponent  
        {...boundActionCreators} 
      /> 
    ) 
  } 

 
function mapStateToProps(state) { 
  return { 
    state 
  } 

export default connect(mapStateToProps)(TodoComponect)  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.

bindActionCreator源码解析 

function bindActionCreator(actionCreator, dispatch) { 
  return (...args) => dispatch(actionCreator(...args)) 

 
// bindActionCreators期待一个Object作为actionCreators传入,里面是 keyaction 
export default function bindActionCreators(actionCreators, dispatch) { 
  // 如果只是传入一个action,则通过bindActionCreator返回被绑定到dispatch的函数 
  if (typeof actionCreators === 'function') { 
    return bindActionCreator(actionCreators, dispatch) 
  } 
 
  if (typeof actionCreators !== 'object' || actionCreators === null) { 
    throw new Error( 
      `bindActionCreators expected an object or a functioninstead received ${actionCreators === null ? 'null' : typeof actionCreators}. ` + 
      `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?` 
    ) 
  } 
 
  // 遍历并通过bindActionCreator分发绑定至dispatch 
  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 
 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

react-redux

Provider 

export default class Provider extends Component { 
  getChildContext() { 
    // 将其声明为 context 的属性之一 
    return { store: this.store } 
  } 
 
  constructor(props, context) { 
    super(props, context) 
    // 接收 redux 的 store 作为 props 
    this.store = props.store 
  } 
 
  render() { 
    return Children.only(this.props.children) 
  } 

 
if (process.env.NODE_ENV !== 'production') { 
  Provider.prototype.componentWillReceiveProps = function (nextProps) { 
    const { store } = this 
    const { store: nextStore } = nextProps 
 
    if (store !== nextStore) { 
      warnAboutReceivingStore() 
    } 
  } 

 
Provider.propTypes = { 
  store: storeShape.isRequired, 
  children: PropTypes.element.isRequired 

Provider.childContextTypes = { 
  store: storeShape.isRequired 
 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.

connect

传入mapStateToProps,mapDispatchToProps,mergeProps,options。

首先获取传入的参数,如果没有则以默认值代替 

const defaultMapStateToProps = state => ({}) // eslint-disable-line no-unused-vars 
const defaultMapDispatchToProps = dispatch => ({ dispatch }) 
const { pure = true, withRef = false } = options  
  • 1.
  • 2.
  • 3.

之后,通过 

const finalMergeProps = mergeProps || defaultMergeProps 
  • 1.

选择合并stateProps,dispatchProps,parentProps的方式,默认的合并方式 defaultMergeProps 为: 

const defaultMergeProps = (stateProps, dispatchProps, parentProps) => ({ 
  ...parentProps, 
  ...stateProps, 
  ...dispatchProps 
}) 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

 返回一个以 Component 作为参数的函数。在这个函数内部,生成了一个叫做Connect的 Component 

// ... 
  return function wrapWithConnect(WrappedComponent) { 
    const connectDisplayName = `Connect(${getDisplayName(WrappedComponent)})` 
    // 检查参数合法性 
    function checkStateShape(props, methodName) {} 
    // 合并props 
    function computeMergedProps(stateProps, dispatchProps, parentProps) { 
      const mergedProps = finalMergeProps(stateProps, dispatchProps, parentProps) 
      if (process.env.NODE_ENV !== 'production') { 
        checkStateShape(mergedProps, 'mergeProps'
      } 
      return mergedProps 
    } 
     
    // start of Connect 
    class Connect extends Component { 
      constructor(props, context) { 
        super(props, context); 
        this.store = props.store || context.store 
         
        const storeState = this.store.getState() 
        this.state = { storeState } 
        this.clearCache() 
      } 
       
      computeStateProps(store, props) { 
        // 调用configureFinalMapState,使用传入的mapStateToProps方法(或默认方法),将state map进props 
      } 
      configureFinalMapState(store, props) {} 
       
      computeDispatchProps(store, props) { 
        // 调用configureFinalMapDispatch,使用传入的mapDispatchToProps方法(或默认方法),将action使用dispatch封装map进props 
      } 
      configureFinalMapDispatch(store, props) {} 
       
      // 判断是否更新props 
      updateStatePropsIfNeeded() {} 
      updateDispatchPropsIfNeeded() {} 
      updateMergedPropsIfNeeded() {} 
       
      componentDidMount() { 
        // 内部调用this.store.subscribe(this.handleChange.bind(this)) 
        this.trySubscribe() 
      } 
      handleChange() { 
        const storeState = this.store.getState() 
        const prevStoreState = this.state.storeState 
        // 对数据进行监听,发送改变时调用 
        this.setState({ storeState }) 
      } 
       
      // 取消监听,清除缓存 
      componentWillUnmount() { 
        this.tryUnsubscribe() 
        this.clearCache() 
      } 
       
      render() { 
        this.renderedElement = createElement(WrappedComponent, 
            this.mergedProps 
        ) 
        return this.renderedElement 
      } 
    } 
    // end of Connect 
     
    Connect.displayName = connectDisplayName 
    Connect.WrappedComponent = WrappedComponent 
    Connect.contextTypes = { 
      store: storeShape 
    } 
    Connect.propTypes = { 
      store: storeShape 
    } 
     
    return hoistStatics(Connect, WrappedComponent) 
  } 
// ...  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.

我们看见,在connect的***,返回了使用hoistStatics包装的Connect和WrappedComponent

hoistStatics是什么鬼?为什么使用它?

Copies non-react specific statics from a child component to a parent component. Similar to Object.assign, but with React static keywords blacklisted from being overridden.

也就是说,它类似于Object.assign,作用是将子组件中的 static 方法复制进父组件,但不会覆盖组件中的关键字方法(如 componentDidMount) 

import hoistNonReactStatic from 'hoist-non-react-statics'
 
hoistNonReactStatic(targetComponent, sourceComponent);  
  • 1.
  • 2.
  • 3.
责任编辑:庞桂玉 来源: segmentfault
相关推荐

2021-05-17 09:50:06

Kubebuilde源码CURD

2020-10-14 11:11:56

Redux源码解析

2021-09-26 08:35:17

Android控件宽高

2021-10-20 07:18:50

开源轻量级缓存

2011-03-23 10:30:01

LAMPApache源码

2021-09-09 06:55:43

AndroidViewDragHel原理

2019-12-23 09:13:11

Python数据语言

2020-06-18 14:39:42

MongoDB数据数据库

2022-04-06 14:55:45

Harmony同步机制鸿蒙

2018-09-06 14:29:13

容器主机存储

2021-03-25 10:14:10

自动化运营人工智能AIOps

2019-04-24 17:45:24

微服务容器青云

2021-07-03 08:51:30

源码Netty选择器

2022-05-24 10:36:45

云原生容器应用

2015-09-24 18:25:23

RxSwift

2021-11-12 16:00:26

鸿蒙HarmonyOS应用

2023-09-06 07:11:41

大模型人工智能

2021-09-02 07:00:01

Glide流程Android

2020-06-19 11:20:17

开发避坑支付宝

2016-12-09 19:21:14

点赞
收藏

51CTO技术栈公众号