Redux入坑进阶之源码解析

  1. 云栖社区>
  2. 博客>
  3. 正文

Redux入坑进阶之源码解析

行者武松 2017-08-01 13:46:00 浏览736
展开阅读全文

预热

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

柯里化函数(curry)

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


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

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

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

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

举个栗子:


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

代码组合(compose)

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


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

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

Redux

combineReducers


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

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

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

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


  1. export default function combineReducers(reducers) { 
  2.   // 第一次筛选,参数reducers为Object 
  3.   // 筛选掉reducers中不是function的键值对 
  4.   var reducerKeys = Object.keys(reducers); 
  5.   var finalReducers = {} 
  6.   for (var i = 0; i < reducerKeys.length; i++) { 
  7.     var key = reducerKeys[i]; 
  8.     if (typeof reducers[key] === 'function') { 
  9.       finalReducers[key] = reducers[key
  10.     } 
  11.   } 
  12.  
  13.   var finalReducerKeys = Object.keys(finalReducers) 
  14.  
  15.   // 二次筛选,判断reducer中传入的值是否合法(!== undefined) 
  16.   // 获取筛选完之后的所有key 
  17.   var sanityError 
  18.   try { 
  19.     // assertReducerSanity函数用于遍历finalReducers中的reducer,检查传入reducer的state是否合法 
  20.     assertReducerSanity(finalReducers) 
  21.   } catch (e) { 
  22.     sanityError = e 
  23.   } 
  24.    
  25.   // 返回一个function。该方法接收state和action作为参数 
  26.   return function combination(state = {}, action) { 
  27.     // 如果之前的判断reducers中有不法值,则抛出错误 
  28.     if (sanityError) { 
  29.       throw sanityError 
  30.     } 
  31.     // 如果不是production环境则抛出warning 
  32.     if (process.env.NODE_ENV !== 'production') { 
  33.       var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action
  34.       if (warningMessage) { 
  35.         warning(warningMessage) 
  36.       } 
  37.     } 
  38.  
  39.     var hasChanged = false 
  40.     var nextState = {} 
  41.     // 遍历所有的key和reducer,分别将reducer对应的key所代表的state,代入到reducer中进行函数调用 
  42.     for (var i = 0; i < finalReducerKeys.length; i++) { 
  43.       var key = finalReducerKeys[i] 
  44.       var reducer = finalReducers[key
  45.       // 这也就是为什么说combineReducers黑魔法--要求传入的Object参数中,reducer function的名称和要和state同名的原因 
  46.       var previousStateForKey = state[key
  47.       var nextStateForKey = reducer(previousStateForKey, action
  48.       // 如果reducer返回undefined则抛出错误 
  49.       if (typeof nextStateForKey === 'undefined') { 
  50.         var errorMessage = getUndefinedStateErrorMessage(keyaction
  51.         throw new Error(errorMessage) 
  52.       } 
  53.       // 将reducer返回的值填入nextState 
  54.       nextState[key] = nextStateForKey 
  55.       // 如果任一state有更新则hasChanged为true 
  56.       hasChanged = hasChanged || nextStateForKey !== previousStateForKey 
  57.     } 
  58.     return hasChanged ? nextState : state 
  59.   } 
  60.  
  61. // 检查传入reducer的state是否合法 
  62. function assertReducerSanity(reducers) { 
  63.   Object.keys(reducers).forEach(key => { 
  64.     var reducer = reducers[key
  65.     // 遍历全部reducer,并给它传入(undefined, action
  66.     // 当第一个参数传入undefined时,则为各个reducer定义的默认参数 
  67.     var initialState = reducer(undefined, { type: ActionTypes.INIT }) 
  68.      
  69.     // ActionTypes.INIT几乎不会被定义,所以会通过switch的default返回reducer的默认参数。如果没有指定默认参数,则返回undefined,抛出错误 
  70.     if (typeof initialState === 'undefined') { 
  71.       throw new Error( 
  72.         `Reducer "${key}" returned undefined during initialization. ` + 
  73.         `If the state passed to the reducer is undefined, you must ` + 
  74.         `explicitly return the initial state. The initial state may ` + 
  75.         `not be undefined.` 
  76.       ) 
  77.     } 
  78.  
  79.     var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.'
  80.     if (typeof reducer(undefined, { type }) === 'undefined') { 
  81.       throw new Error( 
  82.         `Reducer "${key}" returned undefined when probed with a random type. ` + 
  83.         `Don't try to handle ${ActionTypes.INIT} or other actions in "redux/*" ` + 
  84.         `namespace. They are considered private. Instead, you must return the ` + 
  85.         `current state for any unknown actions, unless it is undefined, ` + 
  86.         `in which case you must return the initial state, regardless of the ` + 
  87.         `action type. The initial state may not be undefined.` 
  88.       ) 
  89.     } 
  90.   }) 
  91. }  

createStore


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

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


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

thunkMiddleware

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


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

applyMiddleware

先复习下用法:


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

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

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


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

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

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


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

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


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

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


  1. applyMiddleware(thunkMiddleware)(createStore)(reducer, initialState) 
  • 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 注入到子组件中时,一般会这么做:


  1. import { connect } from 'react-redux'
  2. import {addTodo, deleteTodo} from './action.js'
  3.  
  4. class TodoComponect extends Component { 
  5.   render() { 
  6.     return ( 
  7.       <ChildComponent  
  8.         deleteTodo={this.props.deleteTodo} 
  9.         addTodo={this.props.addTodo} 
  10.       /> 
  11.     ) 
  12.   } 
  13.  
  14. function mapStateToProps(state) { 
  15.   return { 
  16.     state 
  17.   } 
  18. function mapDispatchToProps(dispatch) { 
  19.   return { 
  20.     deleteTodo: (id) => { 
  21.       dispatch(deleteTodo(id)); 
  22.     }, 
  23.     addTodo: (todo) => { 
  24.       dispatch(addTodo(todo)); 
  25.     } 
  26.   } 
  27. export default connect(mapStateToProps, mapDispatchToProps)(TodoComponect);  

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

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


  1. // 在本组件内的应用 
  2.   addTodo(todo) { 
  3.     let action = TodoActions.addTodo(todo); 
  4.     this.props.dispatch(action); 
  5.   } 
  6.    
  7.   deleteTodo(id) { 
  8.     let action = TodoActions.deleteTodo(id); 
  9.     this.props.dispatch(action); 
  10.   } 
  11.    
  12.   render() { 
  13.     let dispatch = this.props.dispatch; 
  14.     // 传递给子组件 
  15.     let boundActionCreators = bindActionCreators(TodoActions, dispatch); 
  16.     return ( 
  17.       <ChildComponent  
  18.         {...boundActionCreators} 
  19.       /> 
  20.     ) 
  21.   } 
  22.  
  23. function mapStateToProps(state) { 
  24.   return { 
  25.     state 
  26.   } 
  27. export default connect(mapStateToProps)(TodoComponect)  

bindActionCreator源码解析


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

react-redux

Provider


  1. export default class Provider extends Component { 
  2.   getChildContext() { 
  3.     // 将其声明为 context 的属性之一 
  4.     return { store: this.store } 
  5.   } 
  6.  
  7.   constructor(props, context) { 
  8.     super(props, context) 
  9.     // 接收 redux 的 store 作为 props 
  10.     this.store = props.store 
  11.   } 
  12.  
  13.   render() { 
  14.     return Children.only(this.props.children) 
  15.   } 
  16.  
  17. if (process.env.NODE_ENV !== 'production') { 
  18.   Provider.prototype.componentWillReceiveProps = function (nextProps) { 
  19.     const { store } = this 
  20.     const { store: nextStore } = nextProps 
  21.  
  22.     if (store !== nextStore) { 
  23.       warnAboutReceivingStore() 
  24.     } 
  25.   } 
  26.  
  27. Provider.propTypes = { 
  28.   store: storeShape.isRequired, 
  29.   children: PropTypes.element.isRequired 
  30. Provider.childContextTypes = { 
  31.   store: storeShape.isRequired 
  32. }  

connect

传入mapStateToProps,mapDispatchToProps,mergeProps,options。

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


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

之后,通过


  1. const finalMergeProps = mergeProps || defaultMergeProps 

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


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

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


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


  1. import hoistNonReactStatic from 'hoist-non-react-statics'
  2.  
  3. hoistNonReactStatic(targetComponent, sourceComponent);  



作者:ecmadao

来源:51CTO

网友评论

登录后评论
0/500
评论
行者武松
+ 关注