[源码]|[源码] Redux React-Redux01

[源码]|[源码] Redux React-Redux01
文章图片

  • redux中间件洋葱模型
    [源码]|[源码] Redux React-Redux01
    文章图片

    [源码]|[源码] Redux React-Redux01
    文章图片
  • redux中间件注意点
    [源码]|[源码] Redux React-Redux01
    文章图片
导航 [[深入01] 执行上下文](https://juejin.im/post/684490...
[[深入02] 原型链](https://juejin.im/post/684490...
[[深入03] 继承](https://juejin.im/post/684490...
[[深入04] 事件循环](https://juejin.im/post/684490...
[[深入05] 柯里化 偏函数 函数记忆](https://juejin.im/post/684490...
[[深入06] 隐式转换 和 运算符](https://juejin.im/post/684490...
[[深入07] 浏览器缓存机制(http缓存机制)](https://juejin.im/post/684490...
[[深入08] 前端安全](https://juejin.im/post/684490...
[[深入09] 深浅拷贝](https://juejin.im/post/684490...
[[深入10] Debounce Throttle](https://juejin.im/post/684490...
[[深入11] 前端路由](https://juejin.im/post/684490...
[[深入12] 前端模块化](https://juejin.im/post/684490...
[[深入13] 观察者模式 发布订阅模式 双向数据绑定](https://juejin.im/post/684490...
[[深入14] canvas](https://juejin.im/post/684490...
[[深入15] webSocket](https://juejin.im/post/684490...
[[深入16] webpack](https://juejin.im/post/684490...
[[深入17] http 和 https](https://juejin.im/post/684490...
[[深入18] CSS-interview](https://juejin.im/post/684490...
[[深入19] 手写Promise](https://juejin.im/post/684490...
[[深入20] 手写函数](https://juejin.im/post/684490...
[[react] Hooks](https://juejin.im/post/684490...
[[部署01] Nginx](https://juejin.im/post/684490...
[[部署02] Docker 部署vue项目](https://juejin.im/post/684490...
[[部署03] gitlab-CI](https://juejin.im/post/684490...
[[源码-webpack01-前置知识] AST抽象语法树](https://juejin.im/post/684490...
[[源码-webpack02-前置知识] Tapable](https://juejin.im/post/684490...
[[源码-webpack03] 手写webpack - compiler简单编译流程](https://juejin.im/post/684490...
[[源码] Redux React-Redux01](https://juejin.im/post/684490...
[[源码] axios ](https://juejin.im/post/684490...
[[源码] vuex ](https://juejin.im/post/684490...
[[源码-vue01] data响应式 和 初始化渲染 ](https://juejin.im/post/684490...
[[源码-vue02] computed 响应式 - 初始化,访问,更新过程 ](https://juejin.im/post/684490...
[[源码-vue03] watch 侦听属性 - 初始化和更新 ](https://juejin.im/post/684490...
[[源码-vue04] Vue.set 和 vm.$set ](https://juejin.im/post/684490...
[[源码-vue05] Vue.extend ](https://juejin.im/post/684490...
[[源码-vue06] Vue.nextTick 和 vm.$nextTick ](https://juejin.im/post/684790...
前置知识 plainObject
  • 纯对象
  • plainObject 是指通过对象字面量方式创建或者通过构造函数创建的对象,即 ( {} ) 或者 ( new Object() ) 方式创建的对象
  • 纯对象如何理解:
    • 比如 Date,Regexp,Function,Array等就不属于纯对象,但是用 typeof 判断他们都会返回 'object'
    • 比如 const a = { name: 'woow_wu7'} 这样声明的对象就是一个纯对象,因为是通过对象字面量声明的对象
context
  • React.createContext(defaultValue)
    • 创建 context 对象
      • context对象上包含
        • Provider 组件
    • const MyContext = React.createContext(defaultValue)
      • 如果React渲染一个 ( 订阅 ) 了 ( MyContext ) 对象的组件时,这个组件会从组件树中离 ( 自身最近 ) 的那个匹配的 ( Provider ) 中读取到当前的 ( context ) 值
      • 参数
        • defaultValue:只要在所处的组件树中没有 Provider 时,defaultValue 才会生效
        • 注意:
          • 中的value是undefined时,defaultValue不生效
  • Context.Provider
    • 参数
      • value
    • 作用
      • 允许消费组件订阅 context 的变化
      • 当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染
      • Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新
  • Class.contextType
    • MyClass.contextType = MyContext
      • 将 MyContext 即一个context对象,赋值给一个组件的 contextType 属性
      • 那么就可以通过在组件内部通过 this.context 获取到 context 的值
        • 可以在组件的任何生命周期中通过 this.context 访问到context的值
  • Context.Consumer
    • 在函数式组件中完成订阅 context
  • 实例
    import React from 'react' import { connect } from 'react-redux' import { bindActionCreators } from 'redux' import * as actions from './action' import MyContext from '../../context' import './index.less'class ContextTest extends React.Component { render() { return (context-test { ({ author, date }) => { console.log('函数组件通过MyContext.Consumer组件包裹函数组件,函数组件的参数就是context的值') return ({ author }) } } ) } } // MyContext.Provider // MyContext.Consumer // Consumer组件用于函数式组件,用Consumer包裹函数组件后,函数组件的参数就是最近的Provider的提供的context的值const mapStateToProps = (state) => { return { ...state } } const mapDispatchToProps = (dispatch) => { return bindActionCreators(actions, dispatch) } export default connect(mapStateToProps, mapDispatchToProps)(ContextTest)class ContextTypeText extends React.Component { // class式组件 render() { const { appName } = this.context console.log(this.context, 'class组件通过class.contextType = MyContext来获取context') return ( <> {appName} ) } } ContextTypeText.contextType = MyContext // Class.contextType = MyContext // 把MyContext赋值组件的contextType后,就可以在组件内通过 this.context 获取context的值

  • 官网 context api讲解
    一些单词
    preload:预载,预装 enhancer:增强器,放大器 bound:一定会,跳跃 nest:巢,嵌套plain:纯的 plainObject:纯对象 invoked: 调用assert:断言

Redux
  • createStore
  • combineReducers
  • applyMiddleware
  • bindActionCreators
  • compose
    React-Reux
  • Provider
  • connect
  • useSelector
  • useDispatch
  • useStore
  • createSelectorHook
  • createDispatchHook
  • createStoreHook
  • shallowEqual shallow是浅的意思
  • batch
  • connectAdvanced
  • ReactReduxContext
2021/01/02复习
  • [[redux和react-redux]源码分析仓库](https://github.com/woow-wu7/7...
[源码]|[源码] Redux React-Redux01
文章图片

按中间件的使用流程来梳理redux源码
(1) index.js - 入口文件 - 使用 react-redux 的 Provider 包裹根组件 - Provider的作用是通过context将store传入子组件,子组件通过 connect 获取 state ReactDOM.render( // --------------------- Provider , document.getElementById('root')); (2) store.js - 创建store实例 const allReducer = { home: HomeReducer, banner: BannerReducer, recommend: RecomendReducer, // ----------------- 用到了redux-thunk } const store = createStore( combineReducers({ // --------------------------- combineReducer 将所有的reducer合成一个总的reducer ...allReducer }), composeWithDevTools( // ------------------------ enhancer增强器 applyMiddleware(thunkMiddleware, loggerMiddleware) // ---------- applyMiddleware 将多个中间件通过洋葱模型执行副作用,并dispatch(action) ) )(3) 在组件模块中 - 通过 connect 连接 store - 将 store state 和 改装过后的 action creators 注入到 props 中传给各组件 const mapStateToProps = (state) => { // ---------------------------------------------- mapStateToProps return { ...state } } const mapDispatchToProps = (dispatch) => { // --------------------------------------- mapDisPatchToProps return bindActionCreators(actions, dispatch) // ----------------------------------- bindActionCreators } export default connect(mapStateToProps, mapDispatchToProps)(Home) // ---------------- connect(4) home/reducer.js import actionType from './constant.js' const initialState = { name: 'wang' } const reducer = function(state=initialState, action) { switch (action.type) { case actionType.GET_USERNAME: return { ...state, name: action.payload } default: return { ...state } } } export default reducer(5) home/action.js import actionType from './constant' export const getUerName = (name)=> { return { type: actionType.GET_USERNAME, payload: name } }(6) home/constant.js const actonType = { GET_USERNAME: 'GET_USERNAME' } export default actonType

redux-thunk中间件源码分析
// redux-thunk 源码// 1 // createThunkMiddleware函数签名如下 // (extraArgument) => ({dispatch, getState}) => next => action => { ...具体根据action的类型来判断 }// 2 // 真正导出的是: // (1) action是一个对象: ({dispatch, getState}) => next => action => next(acion) // (2) action是一个函数: ({dispatch, getState}) => next => action => action(dispath, getState, extraArgument)// 3 // 具体案例在 在 admin-system/interview-react/Knowledge/reduxSourceCode组件中function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { // 如果 action 是一个函数 if (typeof action === 'function') { return action(dispatch, getState, extraArgument); // 比如dispatch这样一个函数 // 1. // 2. const add = () => { dispatch(handleThunk) } // 3. 如下 // const handleThunk = (dispatch: any, getState: any) => { //setTimeout(() => { //dispatch({ //type: actionType.ADD_INTERVIEW, //payload: 1 //}) //}, 2000) // }}// 如果 action 不是函数,就调用 next(action) 将 action对象 传递到下一个中间件 return next(action); }; }const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk;

(1) createStore() createStore(reducer, preloadedState, enhancer)
  • 参数:
    • reducer
      • 根 reducer,可以使用 ( combineReducers ) 将多个reducer组合成一颗总树
      • 纯函数,接受 ( prevState ) 和 ( action ),返回 ( 新的state )
    • preloadedState
      • 初始化state,初始化整个store
      • preload: 预载,预装的意思
    • enhancer
      • enhancer是一个函数,叫做增强器,可以在dispatch一个action到达reducer之前做一些增强处理,如:打印日志,dispatch一个函数等
      • applyMiddleware(中间件1,中间件2...) 就是一个增强函数
  • 返回值:
    • enhancer不存在
      • 返回 store 对象
        • dispatch
        • subscribe
        • getState
        • replaceReducer
        • observable
        • 先走流程,这几个函数后面分析
    • enhancer存在,并且是函数
      • 返回 enhancer(createStore)(reducer, preloadedState)
        • enhancer如果是applyMiddleware()的话,返回值也是一个对象
        • enhancer返回值:
          • 像这样的对象{..store, dispatch}
          • 后面dispatch会覆盖掉store中的dispatch函数,对象中后面的属性会覆盖前面的属性
    createStore.js先分析主要的流程

  • 如果enhancer是函数就返回一个
    • enhancer(createStore)(reducer, preloadedState)
      // 在真实的开发过程中一般都会使用 enhancer 增强器
      // 一般是调用 applyMiddleware(thunkMiddleware)
  • 如果enhancer不存在就返回一个
    • { dispatch,subscribe,getState,replaceReducer,[?observable]: observable } 对象
    createStore.jsexport default function createStore(reducer, preloadedState, enhancer) {
    if (
    (typeof preloadedState === 'function' && typeof enhancer === 'function') ||
    (typeof enhancer === 'function' && typeof arguments[3] === 'function')
    ) {
    throw new Error(
    'It looks like you are passing several store enhancers to ' + 'createStore(). This is not supported. Instead, compose them ' + 'together to a single function.'

    )
    }
    if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
    }
    if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
    throw new Error('Expected the enhancer to be a function.')

    }
    return enhancer(createStore)(reducer, preloadedState)
    }
    if (typeof reducer !== 'function') {
    throw new Error('Expected the reducer to be a function.')
    }
    return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    }
    }
(2) applyMiddleware() applyMiddleware(middleware1, middleware2, ...) 就是 createStore() 的参数中的 enhancer()
  • applyMiddleware() 函数签名
    • (...middlewares) => createStore => (...args) => ({...store, dispatch})
  • applyMiddleware() 返回值
    • 一个对象
    • {...store, dispatch}
  • 在真实的项目中,一般这样使用 createStore 和 applyMiddleware
    • createStore(combineReducers({...rootReducer}), applyMiddleware(thunkMiddleware, loggerMiddleware))
      • 这里已经是调用了applyMiddleware()的第一层,返回的是一个函数
        • 返回的函数签名:createStore => (...args) => ({...store, dispatch})
        • middlewares已经在内存中,形成闭包
  • 实际上第一步是这样的: createStore(rootReducer, (createStore) => (...args) => ({...store, dispatch}))
  • redux中间件
    • redux的中间件是一个高阶函数
    • 函数签名:({dispatch, getState}) => (next) => (action) => next(action)
  • compose()
    • 函数签名:(...funcs) => funcs.reduce((a, b) => (...args) => a(b(...args)))
    • 返回值:一个函数
    • 作用:
      • 从右至左,将右边函数的返回值作为左边函数的参数传入
      • compose函数就是洋葱模型
      • compose(A, B, C)(arg) === A(B(C(arg)))
    • 调用时:dispatch = compose(...chain)(store.dispatch)
    • chain类似:chain = [(next) => (action) => next(action), (next) => (action) => next(action)]
import compose from './compose'export default function applyMiddleware(...middlewares) { // 在createStore()中调用enhancer时,传入的参数是 // enhancer(createStore)(reducer, preloadedState) // 这是一个高阶函数,柯里化函数 // 1) 执行 const resFn = enhancer(createStore),返回值也是一个函数 // 2) 执行 consr res = resFn(reducer, preloadedState) // 最终 applyMiddleware 返回的是 ({...store, dispatch}) 对象return createStore => (...args) => { // 这里的args是一个数组 // 具体的值就是:[reducer,preloadedState] const store = createStore(...args)let dispatch = () => { // 声明dispatch方法 throw new Error( 'Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.' ) }const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } const chain = middlewares.map(middleware => middleware(middlewareAPI)) // 这里middleware数组中有两个中间件:thunkMiddleware,loggerMiddleware// middleware的函数签名如下: // ({dispatch, getState}) => (next) => (action) => next(action)// chain = [(next) => (action) => next(action), (next) => (action) => next(action)] dispatch = compose(...chain)(store.dispatch)// compose // 函数签名:(...funcs) => funcs.reduce((a, b) => (...args) => a(b(...args))) // compose()的作用是:从右至左,将右边函数的返回值作为左边函数的参数传入 // compose(A, B, C)(arg) === A(B(C(arg))) // compose(A, B, C)(arg) === A(B(C(arg))) 这个暂时可以作为结论记住,下面会按步骤拆解// 现在假设有三个中间件 // const M1 = (store) => (next) => (action) => { console.log('A开始'); next(action); console.log('A结束')} // const M2 = (store) => (next) => (action) => { console.log('B开始'); next(action); console.log('B结束')} // const M3 = (store) => (next) => (action) => { console.log('C开始'); next(action); console.log('C结束')} // 注意上面三个中间件的第一个参数 store 中只有getState, dispacth两个函数// chain = [(next)=>(action)=>{M1...}, (next)=>(action)=>{M2...}, (next)=>(action)=>{M3...}] // 现在 dispatch = chain.reduce((a, b) => (...args) => a(b(...args)))(store.dispatch) // dispatch的最终形态 // dispatch = M1(M2(M3(store.dispatchj))) // 注意这里的M1M2M3是去除了(store)参数的这一层的返回的函数 (next)=>(action)=>{M1...} // 以下是具体步骤// 第一步:dispatch = funcs.reduce((a, b) => (...args) => a(b(...args)))(store.dispatch)// 第一次reduce // a是:(next) => (action) => { console.log('A开始'); next(action); console.log('A结束')} // b是:(next) => (action) => { console.log('B开始'); next(action); console.log('B结束')} // b(...args)是:(action) => { console.log('B开始'); (...args)(action); console.log('B结束')} // a(b(...args)) 就是调用a中的 next(actions) 换成next就是 (action) => { console.log('B开始'); (...args)(action); console.log('B结束')}函数,参数是action // a(b(...args))是:(action) => { console.log('A开始'); console.log('B开始'); (...args)(action); console.log('B结束') console.log('A结束')} // 总返回值:(...args) => (action) => { console.log('A开始'); console.log('B开始'); (...args)(action); console.log('B结束') console.log('A结束')}// 第二次reduce // a是:(...args) => (action) => { console.log('A开始'); console.log('B开始'); (...args)(action); console.log('B结束') console.log('A结束')} // b是:(next) => (action) => { console.log('C开始'); next(action); console.log('C结束')} // b(...args)是:(action) => { console.log('C开始'); (...args)(action); console.log('C结束')} // a(b(...args))是:(action) => { console.log('A开始'); console.log('B开始'); console.log('C开始'); (...args)(action); console.log('C结束') console.log('B结束') console.log('A结束')} // 总返回值:(...args) => (action) => { console.log('A开始'); console.log('B开始'); console.log('C开始'); (...args)(action); console.log('C结束') console.log('B结束') console.log('A结束')}// 第二步:将第一步返回的函数 (...args) => (action) => {...(...args)(action); ...} 传入参数 (store.dispatch) 并执行dispatch = (action) => { console.log('A开始'); console.log('B开始'); console.log('C开始'); store.dispatch(action); console.log('C结束') console.log('B结束') console.log('A结束')}// 第二步格式化一下: // dispatch = (action) => { //console.log('A开始') //console.log('B开始') //console.log('C开始') //store.dispatch(action) //console.log('C结束') //console.log('B结束') //console.log('A结束') // } return { ...store, dispatch, // dispatch就是一个函数,经过redux中间件执行各种副作用后,调用store对象上的 dispatch()方法 // 而store对象又是通过 createStore()方法生成的,里面有dispatch()方法的实现// store上的dispatch方法,主要干了两件事情 // 1. 传递action给reducer,更新state // 2. state更新后,执行监听数组中的所有监听函数listener } } }

[源码]|[源码] Redux React-Redux01
文章图片

[源码]|[源码] Redux React-Redux01
文章图片

(3) compose()
  • 比如:compose(a, b, c)(1)
  • 函数签名:(...funcs) => funcs.reduce((a, b) => (...args) => a(b(...args)))
  • 返回值:一个函数,因为可以通过compose()()这样的方式调用,就说明compose()返回了一个函数
  • 作用:
    • 从右至左,将右边函数的返回值作为左边函数的参数传入
    • compose函数就是洋葱模型
    • compose(A, B, C)(arg) === A(B(C(arg)))
// redux4.0.5 // compose源码export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) } // 当compose的参数 // 长度为0时,即没有参数时,返回一个函数,该函数直接将传入的参数返回 // 长度为1时,返回这个参数函数 // 长度大于1时,返回参数数组执行reduce迭代的结果,reduce返回在这里返回的是一个函数// 注意: // compose()的返回值是一个函数,所以相当于高阶函数,可以通过compose(a,b,c)(d)这样的方式调用-- 现在假设有这样一段代码 const a = (number) => number + 1; const b = (number) => number + 2; const c = (number) => number + 3; const res = compose(a, b, c)(1) // compose的参数有三个,所以执行 return funcs.reduce((a, b) => (...args) => a(b(...args))) // 所以 res = [a, b,c].reduce((a, b) => (...args) => a(b(...args)))返回了一个函数, 传入的参数是 1, 并调用执行// 先看 compose(a, b, c) 返回了什么 //[a, b,c].reduce((a, b) => (...args) => a(b(...args)))// 第一步: // 累积变量: a // 当前变量: b // b(...args)返回值:(...args) + 2 // a(b(...args))返回值:(...args) + 2 + 1 // 第一步总返回值:(...args) => (...args) + 2 + 1 注意返回值是一个函数,这个函数并未被调用// 第二步: // 累积变量:(...args) => (...args) + 2 + 1,上一步的返回值作为新的累积变量 // 当前变量: c // c(...args)返回值:(...args) + 3 // ab(c(...args)):即调用ab(),参数是 c(...args)返回值(...args) + 3 // ab函数 (...args) => (...args) + 2 + 1 // ab函数参数 (...args) + 3 //ab函数最终的返回值:(...args) + 3 + 2 + 1 //第二步总返回值:(...args) => (...args) + 3 + 2 + 1// 第三步 const res = compose(a, b, c)(1) // 1. compose(a, b, c)返回了一个函数:(...args) => (...args) + 3 + 2 + 1 // 2. const res = composeRes(1) // 3. const res = 1 + 3 + 2 + 1 // 4. const res = 7console.log(res, 'res') // 7 // 4 + 2 + 1总结: 1. 从以上过程可以看出,只有最右边的中间件可以接收多个参数,左边函数的参数都是右边函数的返回值

  • compose 2020/12/9复习
    redux中的compose函数源码function compose(...funcs) { if (funcs.length === 0) { // ------------- (1) 如果compose函数没有传入参数,就返回一个函数,该函数直接将参数值返回 return arg => arg } if (funcs.length === 1) { // ------------- (2) 如果compose函数传入了一个参数,那么直接返回该函数 return funcs[0] } // ---------------------------------------- (3) 当compose的参数大于1个参数时,返回迭代器reduce,而reduce返回的是一个函数 return funcs.reduce((a, b) => (...args) => a(b(...args))) } const a = (num) => num + 1; const b = (num) => num + 2; const c = (num) => num + 3; const resFn = compose(a, b, c)(1) console.log(resFn, 'resFn')compose分析: (1) 如果compose函数没有传入参数,就返回一个函数,该函数直接将参数值返回 (2) 如果compose函数传入了一个参数,那么直接返回该函数 (3) 当compose的参数大于1个参数时,返回迭代器reduce,而reduce返回的是一个函数

  • compose(a, c, c)
  • 本例中compose函数一共传入了两层参数
    • 第一层:一共传入了三个函数作为参数,分别是 a b c
    • 第二层:传入了参数 1
    第一步:第一层函数被调用 compose(a, b, c)
    [a, b, c].reduce((a, b) => (...args) => a(b(...args)))
    第二步:
    • reduce第一次迭代
    • (...args) => a(b(...args))
      • b(...args)执行的结果作为a的参数 => a((...args) + 2)
      • a((...args) + 2)执行的结果 => (...args) + 2 + 1
    • reduce的总的返回值 => (...args) => (...args) + 2 + 1
      第三步:
    • reduce的第二次迭代
    • (...args) => ab迭代结果(c(...args))
    • c(...args)执行的结果作为 ab迭代结果的参数 => ((...args) + 3) => (...args) + 2 + 1
    • ab((...args) + 3)执行的结果 => (...args) + 3 + 2 + 1
    • reduce的总的返回值 => (...args) => (...args) + 3 + 2 + 1
      第四步:
    • compose的最终形态
    • const composex = (...args) => (...args) + 3 + 2 + 1
      第五步:第二层函数被调用compose(a,b,c)返回的函数在调用(1)
    • composex(1)
    • 返回值 1+3+2+1
    • 7
(4) bindActionCreators()
  • bindActionCreators(actionCreators, dispatch)
  • 参数
    • ( actionCreators ) 是一个 ( 函数 ) 或者一个 ( 对象 )
    • dispatch
  • 返回值
    • 如果参数actionCreators是一个函数
      • 直接返回 bindActionCreator() 函数的执行结果,即一个函数
      • 返回值是:() => dispatch(actionsCreator.apply(this, arguments)) => () => dispatch(action)
    • 如果参数actionCreators是一个对象
      • 返回一个 boundActionCreators 对象,即一个对象
      • 返回值是:{actionCreator的函数名: () => dispatch(actionCreator.apply(this, arguments)), ...}
    • 返回值总结
      • 如果参数actionCreators是一个函数,返回一个函数
      • 如果参数actionCreators是一个对象,返回对象
  • bindActionCreator()
    • 函数签名:(actionCreator, dispatch) => () => dispatch(actionCreator.apply(this, arguments))
    • 简化函数签名:(actionCreator, dispatch) => () => dispatch(action)
    function bindActionCreator(actionCreator, dispatch) { return function() { return dispatch(actionCreator.apply(this, arguments)) } }export default function bindActionCreators(actionCreators, dispatch) { if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch) } // 如果 actionCreators 是一个函数,就直接返回 bindActionCreator(actionCreators, dispatch) 执行的结果 // bindActionCreator(actionCreators, dispatch) // 返回的是一个函数:() => dispatch(actionCreator.apply(this, arguments))// 即:如果参数actionCreators是一个函数,返回下面这样一个函数 // () => dispatch(actionCreator.apply(this, arguments)) // () => dispatch(action) // 因为actionCreator是action创建函数,调用返回一个actionif (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"?` ) } // 如果参数actionCreators不是对象或者为null,则报错// 下面是参数actionCreators是一个对象的情况 const boundActionCreators = {} for (const key in actionCreators) { const actionCreator = actionCreators[key] if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch) // boundActionCreators // key是:函数名 // value是: () => dispatch(actionCreator.apply(this, arguments)) 这样一个函数 // value简化:() => dispatch(action) } } return boundActionCreators }

(5) combineReducers()
  • combineReducers(reducers)
  • 参数
    • reducers 是一个对象
      • key:reducer函数的 函数名
      • value:reducer函数
  • 返回值
    • 返回 combination() 函数
  • combination(state, action)
    • 返回值:
      • 返回一个对象
      • 每个reducer()执行返回的新的state组成的对象
        • key:传入combineReducers(reducers)的reducers对象中的key
        • value: 是state
      • {key1: state1, key2: state2} 这样的对象
    combineReducers() 核心代码export default function combineReducers(reducers) { const reducerKeys = Object.keys(reducers) const finalReducers = {} for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i]if (process.env.NODE_ENV !== 'production') { if (typeof reducers[key] === 'undefined') { // 如果当前环境不是生成环境并且reducers的某个key对应的值是undefined,抛错 warning(`No reducer provided for key "${key}"`) } }if (typeof reducers[key] === 'function') { // reducer是函数,就收集到finalReducers对象中 // 等于过滤出reducers中是函数的reducer finalReducers[key] = reducers[key] } } const finalReducerKeys = Object.keys(finalReducers)return function combination(state = {}, action) { let hasChanged = false const nextState = {} for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] const reducer = finalReducers[key] const previousStateForKey = state[key] const nextStateForKey = reducer(previousStateForKey, action) if (typeof nextStateForKey === 'undefined') { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey // 1. hasChanged = true 则 hasChanged为true // 2. 只要有一次nextStateForKey 和 previousStateForKey不同,就说明整个state不同,hasChanged就为true } hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length return hasChanged ? nextState : state // 如果state变化了,返回新的state (nextState) // 如果state没有变化,返回就的state (state) } }

(6) createStore 中具体的方法
  • 先复习下第一步中的createStore()方法
    • createStore(reducer, preloadedState, enhancer)
      1. 如果enhancer不存在,就返回自己内部定义的几个方法组成的对象:getState, dispatch, subscribe, replaceReducer,observable等
      1. 如果enhancer存在,并且是函数的话,返回返回 enchancer(createStore)(reducer, perloadedState)
        • enhancer()函数声明:以applyMiddleware为例
        • applyMiddleware()的函数声明:(middlewares) => createStore => (...args) => ({...store, dispatch})
        • 在 applyMiddleware()内部也会调用传入的 createStore(reducer, preloadedState) 生成 store
(6-1) createStore - dispatch()
  • dispatch(action)
  • 参数:
    • action对象,必须要有type字段
  • 返回值
    • action
  • 主要作用
    • 1. 将action传递给reducer纯函数,更新state
    • 2. 更新state后,执行监听数组中所有的订阅了该state的监听函数
    dispatch()export default function createStore(reducer, preloadedState, enhancer) { let currentReducer = reducer // 缓存reducer,reducer是传入的参数 let currentState = preloadedState // 缓存preloadedState,preloadedState是传入的参数 let currentListeners = [] // 新建监听者数组 let nextListeners = currentListeners // 赋值,即两个变量指向同一个堆地址,修改相互影响,注意区分赋值,浅拷贝,深拷贝 let isDispatching = false // 标志位,用来标记 dispatch 执行过程function dispatch(action) { // 接收action对象作为参数 if (!isPlainObject(action)) { // 如果不是纯对象抛错 // 纯对象是必须通过对象字面量声明的对象 或者 通过构造函数声明的对象 // 即 {} 或者 new Object() 生成的对象 // 数组,Date对象,Regexp对象,Error对象,Function对象等都不是纯对象 // 这里action不是对象就抛错,如果不是对象,可能是个函数,比如异步提交的函数,就需要通过redux中间件处理 throw new Error( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ) }if (typeof action.type === 'undefined') { // action对象中必须要有type字段 throw new Error( 'Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?' ) }if (isDispatching) { // 如果正在dispatching,抛错 throw new Error('Reducers may not dispatch actions.') }try { isDispatching = true currentState = currentReducer(currentState, action) // reducer函数的作用就是:接收prevState和action,返回一个新的state // 这里将新的state赋值给变量 } finally { isDispatching = false }const listeners = (currentListeners = nextListeners) for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() // 当reducer纯函数更新了最新的state后,会执行监听数组中的所有监听函数 }return action }return { dispatch, } }

(6-2) createStore - subscribe()
  • 参数
    • listener 监听函数
  • 返回值
    • 返回 unSubscribe() 函数
    • unSubscribe()负责取消订阅监听事件
  • 主要作用
    • 添加监听函数 listener
    • 返回取消监听的函数 unSubscribe
    subscribe()export default function createStore(reducer, preloadedState, enhancer) { let currentListeners = [] let nextListeners = currentListeners // 赋值,修改相互影响,两个变量指针指向同一个堆地址let isDispatching = false // isDispatching 初始值为false // isDispatching // true:在dispatch()中调用reducer()前isDispatching会被修改为true // false:更新完state后isDispatching会被修改为false function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() // 做一层浅拷贝 // 当两个对象的属性值是基本类型的数据时,修改互不影响 // 注意区分赋值,浅拷贝,深拷贝的区别 } }function subscribe(listener) { if (typeof listener !== 'function') { // listener必须是个函数 throw new Error('Expected the listener to be a function.') }if (isDispatching) { // 只执行dispatch过程中,不允许subscribbr throw new Error( 'You may not call store.subscribe() while the reducer is executing. ' + 'If you would like to be notified after the store has been updated, subscribe from a ' + 'component and invoke store.getState() in the callback to access the latest state. ' + 'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.' ) }let isSubscribed = trueensureCanMutateNextListeners() nextListeners.push(listener) // 添加监听函数到nextListeners // 保证 nextListeners 和 currentListeners 两个数组中是不同的listenerreturn function unsubscribe() { if (!isSubscribed) { // 没有监听函数直接返回 return }if (isDispatching) { // 在dispatch()执行时,不能取消订阅 // 因为idispatch(action)主要做两件事情 // 1. 将action传递给reducer纯函数,去更新state // 2. state更新之后,去执行监听数组中的所有监听函数 throw new Error( 'You may not unsubscribe from a store listener while the reducer is executing. ' + 'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.' ) }isSubscribed = falseensureCanMutateNextListeners() const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) // 从nextListeners中删除该 listener } }return { subscribe, } }

(6-3) createStore - getState()
getState()function getState() { if (isDispatching) { throw new Error( 'You may not call store.getState() while the reducer is executing. ' + 'The reducer has already received the state as an argument. ' + 'Pass it down from the top reducer instead of reading it from the store.' ) }return currentState }

(6-4) createStore - replaceReducer()
replaceReducer()function replaceReducer(nextReducer) { if (typeof nextReducer !== 'function') { throw new Error('Expected the nextReducer to be a function.') } currentReducer = nextReducer // 直接赋值传入的新的nextReducer dispatch({ type: ActionTypes.REPLACE }) // 触发内置的replace事件 // 即执行dispatch()方法,action是 { type: ActionTypes.REPLACE } }

React-Redux Provider
  • 主要的作用:
    • 把 store 通过context的方法传递给Context.Provider组件包裹的子组件
    • 在 Provider 组件内部,判断 store.getState() 获取的 state 是否变化,变化了通过setState()更新storeState
    • setState更新最新的store中的state后,Provider组件包裹的所有 ( 子组件 ) 就会 ( 重新渲染 )
    import React, { Component } from 'react' import PropTypes from 'prop-types' import { ReactReduxContext } from './Context'class Provider extends Component { constructor(props) { super(props)const { store } = props // 从 props中获取 store // Provider组件中是具有 store属性的 // this.state = { storeState: store.getState(), store } }componentDidMount() { this._isMounted = true // _isMounted 表示是否挂载 // _isMounted 在 componentDidMount 中为 true // _isMounted 在 componentWillUnmount 中为 false,卸载了this.subscribe() // 调用该组件中自定义的方法subscribe // 该方法的主要作用: // 比对是不是最新的storeState,如果storeState变化了,就用setState()更新state // state更新后,所有子组件就都会重新渲染 }componentWillUnmount() { if (this.unsubscribe) this.unsubscribe() // 卸载时,取消订阅this._isMounted = false }componentDidUpdate(prevProps) { // componentDidUpdate(prevProps, prevState) if (this.props.store !== prevProps.store) { // 更新后,store变化,取消订阅 if (this.unsubscribe) this.unsubscribe()this.subscribe() // 调用该组件中自定义的方法subscribe // 该方法的主要作用: // 比对是不是最新的storeState,如果storeState变化了,就用setState()更新state // state更新后,所有子组件就都会重新渲染 } }subscribe() { const { store } = this.propsthis.unsubscribe = store.subscribe(() => { // 定义unsubscribe取消订阅的方法 // store.subscribe(listener) // 参数 // 参数是一个监听函数 // 返回值 // 返回 unsubscribe const newStoreState = store.getState()if (!this._isMounted) { return }this.setState(providerState => { // If the value is the same, skip the unnecessary state update. if (providerState.storeState === newStoreState) { // storeState 没有变化,就直接return,使用以前的 storeState return null }return { storeState: newStoreState } // storeState变化了,使用最新的newStoreState,更新storeState }) })// Actions might have been dispatched between render and mount - handle those const postMountStoreState = store.getState() if (postMountStoreState !== this.state.storeState) { this.setState({ storeState: postMountStoreState }) // storeState变化就更新为最新的tore.getState()的值 } }render() { const Context = this.props.context || ReactReduxContext // ReactReduxContext 是通过 React.createContext(null)生成的 Context 对象 // 提供 context 给 Context.Provider 包裹的所有子组件 // calss类型的组件,在 class.contextType = Context 后,通过this.context获取 context的值 // function类型的组件,可以通过 Context.Provider 和 Context.Consumer 这样的方式,包裹function组件后,在参数中获取 contextreturn ( {this.props.children} ) } }Provider.propTypes = { store: PropTypes.shape({ subscribe: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired, getState: PropTypes.func.isRequired }), context: PropTypes.object, children: PropTypes.any }export default Provider

connect
  • connect([mapStateToProps],[mapDispatchToProps],[mergeProps],[options])(component)
  • 函数较多,慢慢读吧
项目源码 - 源码分析的地址
  • 7-react-admin-ts源码分析redux react-redux
  • 具体文件夹:src => SOURCE-CODE-ANALYSIS => REDUX文件夹中
资料 【[源码]|[源码] Redux React-Redux01】redux源码 https://juejin.im/post/684490...
图解洋葱模型(中间件的原理,重要)(这个老哥是真的强啊) https://juejin.im/post/684490...
逐行阅读redux源码(一) createStore https://juejin.im/post/684490...
reacr-redux => Provider https://juejin.im/post/684490...
react-redux 庖丁解牛 https://juejin.im/post/684490...
reacr-redux => connect https://github.com/dwqs/blog/...

    推荐阅读