https://github.com/reduxjs/redux 版本 4.0.0

先了解一下redux是怎么用的,此处摘抄自阮一峰老师的《Redux 入门教程

  1. // Web 应用是一个状态机,视图与状态是一一对应的
  2. // 所有的状态,保存在一个对象里面
  3.  
  4. // store 是保存数据的地方
  5.  
  6. // 创建 store
  7. import { createStore } from 'redux'
  8. const store = createStore(fn)
  9.  
  10. // state 是某一时刻 store 的快照,一个 state 对应一个 view
  11. // 可通过 getState() 获取
  12. const state = store.getState()
  13.  
  14. // Action 是一个对象 用来表示 view 发出的改变 state 的通知
  15. // type 是必须的 其他属性可以自由设置
  16. const action = {
  17. type: 'ADD_TODO',
  18. payload: 'Learn Redux'
  19. }
  20.  
  21. // 同一种类型的 action 可以写一个函数生成
  22. const ADD_TODO = '添加 TODO'
  23. // 生成 action 的函数: Action Creator
  24. function addTodo(text) {
  25. return {
  26. type: ADD_TODO,
  27. text
  28. }
  29. }
  30.  
  31. const action = addTodo('Learn Redux')
  32.  
  33. // store.dispatch()是 View 发出 Action 的唯一方法。
  34. store.dispatch(action)
  35.  
  36. // reducer 是 store 接收 state 返回新的 state 的过程
  37.  
  38. const defaultState = 0
  39. // reducer 接收 action 返回新的 state
  40. const reducer = (state = defaultState, action) => {
  41. switch(action.type) {
  42. case: 'ADD':
  43. return state + action.payload
  44. default:
  45. return state
  46. }
  47. }
  48. const state = reducer(1, {
  49. type: 'ADD',
  50. payload: 2
  51. })
  52.  
  53. // 创建 store 时传入 reducer 当调用 store.dispatch 时将自动调用 reducer
  54. const store = createStore(reducer)
  55.  
  56. /*
  57. reducer 是一个纯函数,纯函数要求:
  58. - 不得改写参数
  59. - 不能调用系统 I/O 的API
  60. - 不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果
  61. */
  62.  
  63. // store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数
  64. // 返回解除监听函数
  65. let unsubscribe = store.subsribe(() => { console.log(store.getState) })
  66. unsubscribe() // 解除监听
  67.  
  68. /*
  69. store 提供的三个方法
  70. - store.getState()
  71. - store.dispatch()
  72. - store.subscribe()
  73. */
  74.  
  75. // createStore方法还可以接受第二个参数,表示 State 的最初状态。这通常是服务器给出的。
  76. // !这个初始值会覆盖 Reducer 函数默认的初始值
  77. let store = createStore(todoApp, STATE_FROM_SERVER)
  78.  
  79. // createStore 的简单实现
  80. const createStore = (reducer) => {
  81. let state
  82. let listeners = []
  83.  
  84. const getState = () => state
  85.  
  86. const dispatch = action => {
  87. state = reducer(state, action)
  88. listeners.forEach(listener => listener())
  89. }
  90.  
  91. const subscribe = listener => {
  92. listeners.push(listener)
  93. return () => {
  94. listeners = listeners.filter(l => l !== listener)
  95. }
  96. }
  97.  
  98. dispatch({})
  99.  
  100. return { getState, dispatch, subscribe }
  101.  
  102. }
  103.  
  104. // 可以通过 combineReducers 来将多个 Reducer 合为一个
  105. import { combineReducers } from 'redux'
  106.  
  107. const chatReducer = combineReducers({
  108. chatLog,
  109. statusMessage,
  110. userName
  111. })
  112.  
  113. // combineReducer 的简单实现
  114. const combineReducers = reducers => {
  115. return (state = {}, action) =>
  116. Object.keys(reducers).reduce(
  117. (nextState, key) => {
  118. nextState[key] = reducers[key](state[key], action)
  119. return nextState
  120. },
  121. {}
  122. )
  123. }

工作流程

  1. Redux Flow
  2.  
  3. dispatch(action) (previousState, action)
  4. Action Creators ======> Store ======> Reducers
  5. ^ || <======
  6. \_ || (newState)
  7. \_ (state) ||
  8. \_ ||
  9. (view opt)\_ \/
  10. \--- React Comonents

OK 可以开始看源码了~ 网上Redux源码分析的博客真的非常多.. 不过当你知道他的源码究竟有多短 就能理解了hhh

combineReducers.js

代码一共179行 多是错误处理 我先将错误处理全部删掉 便只剩28行.....

思路就是创建一个对象 将 Reducer 全部放进去

当Action传进来的时候 就让每一个Reducer去处理这个action

每个Reducer都有一个对应的key 只处理state中对应字段 state[key] 没有Reducer对应的字段会被忽略

截取出核心代码 + 用法、感觉并不需要注释、逻辑都很直接

  1. function combineReducers(reducers) {
  2. const reducerKeys = Object.keys(reducers)
  3. const finalReducers = {}
  4. for (let i = 0; i < reducerKeys.length; i++) {
  5. const key = reducerKeys[i]
  6.  
  7. if (typeof reducers[key] === 'function') {
  8. finalReducers[key] = reducers[key]
  9. }
  10. }
  11. const finalReducerKeys = Object.keys(finalReducers)
  12.  
  13. return function combination(state = {}, action) {
  14. let hasChanged = false
  15. const nextState = {}
  16. for (let i = 0; i < finalReducerKeys.length; i++) {
  17. const key = finalReducerKeys[i]
  18. const reducer = finalReducers[key]
  19. const previousStateForKey = state[key]
  20. const nextStateForKey = reducer(previousStateForKey, action)
  21.  
  22. nextState[key] = nextStateForKey
  23. hasChanged = hasChanged || nextStateForKey !== previousStateForKey
  24. }
  25. // 如果state每一个key都没有被修改 就直接返回原state
  26. return hasChanged ? nextState : state
  27. }
  28. }
  29.  
  30. /***************** 下面是简单的用法实例 *****************/
  31. function todos(state = [], action) {
  32. switch (action.type) {
  33. case 'ADD_TODO':
  34. return state.concat(action.text)
  35. default:
  36. return state
  37. }
  38. }
  39.  
  40. function counter(state = 0, action) {
  41. switch (action.type) {
  42. case 'INCREMENT':
  43. return state + 1
  44. case 'DECREMENT':
  45. return state - 1
  46. default:
  47. return state
  48. }
  49. }
  50.  
  51. let reducer = combineReducers({ list: todos, number: counter })
  52. let state = { list: [], number: 0, otherKey: 'no reducer match will be ignore' }
  53. console.log(state) // { list: [], number: 0, otherKey: 'no reducer match will be ignore' }
  54. state = reducer(state, { type: 'ADD_TODO', text: 'study' })
  55. console.log(state) // { list: [ 'study' ], number: 0 }
  56. state = reducer(state, { type: 'ADD_TODO', text: 'sleep' })
  57. console.log(state) // { list: [ 'study', 'sleep' ], number: 0 }
  58. state = reducer(state, { type: 'INCREMENT' })
  59. console.log(state) // { list: [ 'study', 'sleep' ], number: 1 }

combineReducers.js 源码

  1. import ActionTypes from './utils/actionTypes'
  2. import warning from './utils/warning'
  3. import isPlainObject from './utils/isPlainObject'
  4.  
  5. function getUndefinedStateErrorMessage(key, action) {
  6. const actionType = action && action.type
  7. const actionDescription =
  8. (actionType && `action "${String(actionType)}"`) || 'an action'
  9.  
  10. return (
  11. `Given ${actionDescription}, reducer "${key}" returned undefined. ` +
  12. `To ignore an action, you must explicitly return the previous state. ` +
  13. `If you want this reducer to hold no value, you can return null instead of undefined.`
  14. )
  15. }
  16.  
  17. function getUnexpectedStateShapeWarningMessage(
  18. inputState,
  19. reducers,
  20. action,
  21. unexpectedKeyCache
  22. ) {
  23. const reducerKeys = Object.keys(reducers)
  24. const argumentName =
  25. action && action.type === ActionTypes.INIT
  26. ? 'preloadedState argument passed to createStore'
  27. : 'previous state received by the reducer'
  28.  
  29. if (reducerKeys.length === 0) {
  30. return (
  31. 'Store does not have a valid reducer. Make sure the argument passed ' +
  32. 'to combineReducers is an object whose values are reducers.'
  33. )
  34. }
  35.  
  36. if (!isPlainObject(inputState)) {
  37. // 希望 inputState 是一个简单对象:通过 new Object() 、 {} 创建 (Object.create(null) 这里好像是不合法的
  38. // [object Array] 中提取 'Array'
  39. // Object.prototype.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1]
  40. return (
  41. `The ${argumentName} has unexpected type of "` +
  42. {}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
  43. `". Expected argument to be an object with the following ` +
  44. `keys: "${reducerKeys.join('", "')}"`
  45. )
  46. }
  47. // 检查所有Reducer都没有处理到的key ( 此处实在不解 unexpectedKeyCache 到底何用= =
  48. const unexpectedKeys = Object.keys(inputState).filter(
  49. key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]
  50. )
  51.  
  52. unexpectedKeys.forEach(key => {
  53. unexpectedKeyCache[key] = true
  54. })
  55. // 替换 store 的 Reducer 时会调用 dispatch({ type: ActionTypes.REPLACE })
  56. if (action && action.type === ActionTypes.REPLACE) return
  57.  
  58. if (unexpectedKeys.length > 0) {
  59. return (
  60. `Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` +
  61. `"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` +
  62. `Expected to find one of the known reducer keys instead: ` +
  63. `"${reducerKeys.join('", "')}". Unexpected keys will be ignored.`
  64. )
  65. }
  66. }
  67.  
  68. function assertReducerShape(reducers) {
  69. Object.keys(reducers).forEach(key => {
  70. const reducer = reducers[key]
  71. const initialState = reducer(undefined, { type: ActionTypes.INIT })
  72. // Reducer"$ {key}"在初始化时返回undefined。如果传递给reducer的状态未定义,你必须明确返回初始状态。
  73. // 初始状态可以是不可定义。如果你不想为这个reducer设置一个值,你可以使用null而不是undefined。
  74. if (typeof initialState === 'undefined') {
  75. throw new Error(
  76. `Reducer "${key}" returned undefined during initialization. ` +
  77. `If the state passed to the reducer is undefined, you must ` +
  78. `explicitly return the initial state. The initial state may ` +
  79. `not be undefined. If you don't want to set a value for this reducer, ` +
  80. `you can use null instead of undefined.`
  81. )
  82. }
  83.  
  84. if (
  85. typeof reducer(undefined, {
  86. type: ActionTypes.PROBE_UNKNOWN_ACTION()
  87. }) === 'undefined'
  88. ) {
  89. // 当使用随机类型探测Reducer${key}时返回undefined。
  90. // 不要试图处理${ActionTypes.INIT}或者其他在"redux/*"命名空间的动作。它们被认为是私有的。
  91. // 相反,当你遇到任何未知动作时,你必须返回当前的state,除非当前state是undefined,
  92. // 那样你要返回初始状态,而不管动作类型。初始状态不可以是undefined,但可以为null
  93. throw new Error(
  94. `Reducer "${key}" returned undefined when probed with a random type. ` +
  95. `Don't try to handle ${
  96. ActionTypes.INIT
  97. } or other actions in "redux/*" ` +
  98. `namespace. They are considered private. Instead, you must return the ` +
  99. `current state for any unknown actions, unless it is undefined, ` +
  100. `in which case you must return the initial state, regardless of the ` +
  101. `action type. The initial state may not be undefined, but can be null.`
  102. )
  103. }
  104. })
  105. }
  106.  
  107. /**
  108. * Turns an object whose values are different reducer functions, into a single
  109. * reducer function. It will call every child reducer, and gather their results
  110. * into a single state object, whose keys correspond to the keys of the passed
  111. * reducer functions.
  112. *
  113. * @param {Object} reducers An object whose values correspond to different
  114. * reducer functions that need to be combined into one. One handy way to obtain
  115. * it is to use ES6 `import * as reducers` syntax. The reducers may never return
  116. * undefined for any action. Instead, they should return their initial state
  117. * if the state passed to them was undefined, and the current state for any
  118. * unrecognized action.
  119. *
  120. * @returns {Function} A reducer function that invokes every reducer inside the
  121. * passed object, and builds a state object with the same shape.
  122. */
  123. export default function combineReducers(reducers) {
  124. const reducerKeys = Object.keys(reducers)
  125. const finalReducers = {}
  126. for (let i = 0; i < reducerKeys.length; i++) {
  127. const key = reducerKeys[i]
  128.  
  129. if (process.env.NODE_ENV !== 'production') {
  130. if (typeof reducers[key] === 'undefined') {
  131. warning(`No reducer provided for key "${key}"`)
  132. }
  133. }
  134.  
  135. if (typeof reducers[key] === 'function') {
  136. finalReducers[key] = reducers[key]
  137. }
  138. }
  139. const finalReducerKeys = Object.keys(finalReducers)
  140.  
  141. let unexpectedKeyCache
  142. if (process.env.NODE_ENV !== 'production') {
  143. unexpectedKeyCache = {}
  144. }
  145.  
  146. let shapeAssertionError
  147. try {
  148. // 判断每个reducer都有初始值和对于未知action返回原state
  149. assertReducerShape(finalReducers)
  150. } catch (e) {
  151. shapeAssertionError = e
  152. }
  153.  
  154. return function combination(state = {}, action) {
  155. if (shapeAssertionError) {
  156. throw shapeAssertionError
  157. }
  158.  
  159. if (process.env.NODE_ENV !== 'production') {
  160. const warningMessage = getUnexpectedStateShapeWarningMessage(
  161. state,
  162. finalReducers,
  163. action,
  164. unexpectedKeyCache
  165. )
  166. if (warningMessage) {
  167. warning(warningMessage)
  168. }
  169. }
  170.  
  171. let hasChanged = false
  172. const nextState = {}
  173. for (let i = 0; i < finalReducerKeys.length; i++) {
  174. const key = finalReducerKeys[i]
  175. const reducer = finalReducers[key]
  176. const previousStateForKey = state[key]
  177. const nextStateForKey = reducer(previousStateForKey, action)
  178. if (typeof nextStateForKey === 'undefined') {
  179. const errorMessage = getUndefinedStateErrorMessage(key, action)
  180. throw new Error(errorMessage)
  181. }
  182. nextState[key] = nextStateForKey
  183. hasChanged = hasChanged || nextStateForKey !== previousStateForKey
  184. }
  185. // 如果state每一个key都没有被修改 就直接返回原state
  186. return hasChanged ? nextState : state
  187. }
  188. }

utils/actionTypes.js

  1. // 生成随机字符串的方式可以参考下
  2. // 随机数转36进制 可以生成 '0-9a-z' 的随机字符串
  3. const randomString = () =>
  4. Math.random()
  5. .toString(36)
  6. .substring(7)
  7. .split('')
  8. .join('.')
  9. // 私有action类型 (其实就相当于未知的action 返回当前状态就好了
  10. // 如果当前 state 为undefined 就返回 Reducer设置的初始 state
  11. const ActionTypes = {
  12. INIT: `@@redux/INIT${randomString()}`,
  13. REPLACE: `@@redux/REPLACE${randomString()}`,
  14. PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}`
  15. }
  16.  
  17. export default ActionTypes

createStore.js 

是redux核心代码,不过这个没有什么难理解的地方

  1. import $$observable from 'symbol-observable'
  2.  
  3. import ActionTypes from './utils/actionTypes'
  4. import isPlainObject from './utils/isPlainObject'
  5.  
  6. // 创建 store 的函数
  7. // preloadedState: store设置的初始值 这个值会覆盖 Reducer 的默认值
  8. // 如果使用了 combineReducers preloadedState 要和 combineReducers 有相同的keys
  9. // enhancer: 中间件
  10. export default function createStore(reducer, preloadedState, enhancer) {
  11. // preloadedState可以不传 判断preloadedState是否存在
  12. if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
  13. enhancer = preloadedState
  14. preloadedState = undefined
  15. }
  16.  
  17. if (typeof enhancer !== 'undefined') {
  18. if (typeof enhancer !== 'function') {
  19. throw new Error('Expected the enhancer to be a function.')
  20. }
  21. // enhancer是一个高阶函数 调用enhancer返回一个"加强版"的createStore
  22. return enhancer(createStore)(reducer, preloadedState)
  23. }
  24.  
  25. if (typeof reducer !== 'function') {
  26. throw new Error('Expected the reducer to be a function.')
  27. }
  28.  
  29. let currentReducer = reducer
  30. let currentState = preloadedState
  31. let currentListeners = []
  32. let nextListeners = currentListeners
  33. let isDispatching = false
  34. // 判断当前 nextListeners 和 currentListeners 是否为同一个对象
  35. // 如果是一个对象 就把 nextListeners 改为 currentListeners 的副本
  36. function ensureCanMutateNextListeners() {
  37. if (nextListeners === currentListeners) {
  38. nextListeners = currentListeners.slice()
  39. }
  40. }
  41. // 获取当前对象 如果是正在派发action 则不能获取state
  42. function getState() {
  43. if (isDispatching) {
  44. throw new Error(
  45. 'You may not call store.getState() while the reducer is executing. ' +
  46. 'The reducer has already received the state as an argument. ' +
  47. 'Pass it down from the top reducer instead of reading it from the store.'
  48. )
  49. }
  50.  
  51. return currentState
  52. }
  53. // 订阅 添加订阅者
  54. function subscribe(listener) {
  55. if (typeof listener !== 'function') {
  56. throw new Error('Expected the listener to be a function.')
  57. }
  58.  
  59. if (isDispatching) {
  60. throw new Error(
  61. 'You may not call store.subscribe() while the reducer is executing. ' +
  62. 'If you would like to be notified after the store has been updated, subscribe from a ' +
  63. 'component and invoke store.getState() in the callback to access the latest state. ' +
  64. 'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
  65. )
  66. }
  67.  
  68. let isSubscribed = true
  69. // 每次修改 nextListeners 都要判断一下 nextListeners 和 currentListeners 是否为同一个对象
  70. ensureCanMutateNextListeners()
  71. // 注意 这里修改 nextListeners 之后并没有改变 currentListeners 而是在下一次用到 currentListeners 才会改变
  72. nextListeners.push(listener)
  73.  
  74. // 返回一个当前监听者取消订阅的方法
  75. return function unsubscribe() {
  76. if (!isSubscribed) {
  77. return
  78. }
  79. // 正在派发 action 时不能进行操作
  80. if (isDispatching) {
  81. throw new Error(
  82. 'You may not unsubscribe from a store listener while the reducer is executing. ' +
  83. 'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
  84. )
  85. }
  86.  
  87. isSubscribed = false
  88.  
  89. ensureCanMutateNextListeners()
  90. const index = nextListeners.indexOf(listener)
  91. nextListeners.splice(index, 1)
  92. }
  93. }
  94.  
  95. function dispatch(action) {
  96. if (!isPlainObject(action)) {
  97. throw new Error(
  98. 'Actions must be plain objects. ' +
  99. 'Use custom middleware for async actions.'
  100. )
  101. }
  102.  
  103. if (typeof action.type === 'undefined') {
  104. throw new Error(
  105. 'Actions may not have an undefined "type" property. ' +
  106. 'Have you misspelled a constant?'
  107. )
  108. }
  109.  
  110. if (isDispatching) {
  111. throw new Error('Reducers may not dispatch actions.')
  112. }
  113.  
  114. try {
  115. // 用 isDispatching 记录是否正在 派发action 过程中不能进行其他操作
  116. isDispatching = true
  117. currentState = currentReducer(currentState, action)
  118. } finally {
  119. isDispatching = false
  120. }
  121. // 用到 listeners 才会修改 currentListeners 以减少修改次数
  122. const listeners = (currentListeners = nextListeners)
  123. for (let i = 0; i < listeners.length; i++) {
  124. const listener = listeners[i]
  125. listener()
  126. }
  127.  
  128. return action
  129. }
  130.  
  131. // 替换 Reducer 并派发动作 ActionTypes.REPLACE 相当于对state重新进行初始化
  132. function replaceReducer(nextReducer) {
  133. if (typeof nextReducer !== 'function') {
  134. throw new Error('Expected the nextReducer to be a function.')
  135. }
  136.  
  137. currentReducer = nextReducer
  138. dispatch({ type: ActionTypes.REPLACE })
  139. }
  140. // emmmm...看不懂这个 可以参考 https://distums.github.io/2017/03/19/observables-proposal-for-ecmascript/
  141. function observable() {
  142. const outerSubscribe = subscribe
  143. return {
  144. subscribe(observer) {
  145. if (typeof observer !== 'object' || observer === null) {
  146. throw new TypeError('Expected the observer to be an object.')
  147. }
  148.  
  149. function observeState() {
  150. if (observer.next) {
  151. observer.next(getState())
  152. }
  153. }
  154.  
  155. observeState()
  156. const unsubscribe = outerSubscribe(observeState)
  157. return { unsubscribe }
  158. },
  159.  
  160. [$$observable]() {
  161. return this
  162. }
  163. }
  164. }
  165.  
  166. dispatch({ type: ActionTypes.INIT })
  167.  
  168. return {
  169. dispatch,
  170. subscribe,
  171. getState,
  172. replaceReducer,
  173. [$$observable]: observable
  174. }
  175. }

bindActionCreators.js

此处参考 《mapStateToProps,mapDispatchToProps的使用姿势

按注释上说 这只是一个 convenience method

你可以把 store.dispatch(MyActionCreators.doSomething()) 换成一个转成一个函数

我们使用 action 时 是先通过 actionCreator创建action 然后通过 dispatch 派发出去

通过 bindActionCreator(actionCreator, dispatch) 获得一个可以直接创建action并派发的函数

bindActionCreators 就是创建一个对象 每个属性都是一个 可以直接创建action并派发的函数

例:

  1. action.increase = (info) => { type:'INCREASE'info }
  2. action.decrease = (info) => { type:'DECREASE'info }
  3.  
  4. bindActionCreators({
  5. increase: action.increase,
  6. decrease: action.decrease
  7. }, dispatch)
  8.  
  9. // 就可以获得:
  10. {
  11. increase: (...args) => dispatch(action.increase(...args)),
  12. decrease: (...args) => dispatch(action.decrease(...args))
  13. }

源码:

  1. function bindActionCreator(actionCreator, dispatch) {
  2. return function() {
  3. return dispatch(actionCreator.apply(this, arguments))
  4. }
  5. }
  6.  
  7. /**
  8. * Turns an object whose values are action creators, into an object with the
  9. * same keys, but with every function wrapped into a `dispatch` call so they
  10. * may be invoked directly. This is just a convenience method, as you can call
  11. * `store.dispatch(MyActionCreators.doSomething())` yourself just fine.
  12. *
  13. * For convenience, you can also pass a single function as the first argument,
  14. * and get a function in return.
  15. *
  16. * @param {Function|Object} actionCreators An object whose values are action
  17. * creator functions. One handy way to obtain it is to use ES6 `import * as`
  18. * syntax. You may also pass a single function.
  19. *
  20. * @param {Function} dispatch The `dispatch` function available on your Redux
  21. * store.
  22. *
  23. * @returns {Function|Object} The object mimicking the original object, but with
  24. * every action creator wrapped into the `dispatch` call. If you passed a
  25. * function as `actionCreators`, the return value will also be a single
  26. * function.
  27. */
  28. export default function bindActionCreators(actionCreators, dispatch) {
  29. // 如果 actionCreators 是一个函数 说明只有一个 actionCreator
  30. if (typeof actionCreators === 'function') {
  31. return bindActionCreator(actionCreators, dispatch)
  32. }
  33.  
  34. if (typeof actionCreators !== 'object' || actionCreators === null) {
  35. throw new Error(
  36. `bindActionCreators expected an object or a function, instead received ${
  37. actionCreators === null ? 'null' : typeof actionCreators
  38. }. ` +
  39. `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
  40. )
  41. }
  42.  
  43. const keys = Object.keys(actionCreators)
  44. const boundActionCreators = {}
  45. for (let i = 0; i < keys.length; i++) {
  46. const key = keys[i]
  47. const actionCreator = actionCreators[key]
  48. if (typeof actionCreator === 'function') {
  49. boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
  50. }
  51. }
  52. return boundActionCreators
  53. }

applyMiddleware.js

这个应该是最难理解的部分 所以放到最后看>_<

个人理解,这个东西就是在dispatch前后做一些事情=.= 类似koa express的中间件嘛

以下参考 源码中 redux/docs/advanced/Middleware.md

middleware 在dispatch和action之间提供一个第三方程序扩展点。

现在一步一步理解applyMiddleware在做什么

首先,假设现在有一个需求,每次dispatch一个action时,都要打印action和state,像下面这样:

  1. const action = addTodo('Use Redux')
  2.  
  3. console.log('dispatching', action)
  4. store.dispatch(action)
  5. console.log('next state', store.getState())

但是不可能每一次都这样打印,也许直接修改dispatch就可以

  1. const next = store.dispatch
  2. store.dispatch = function dispatchAndLog(action) {
  3. console.log('dispatching', action)
  4. let result = next(action)
  5. console.log('next state', store.getState())
  6. return result
  7. }

呐,可能不止一个需求,现在我又想记录错误信息了。我们写两个方法,分别给dispatch添加自己想要的功能。

  1. function patchStoreToAddLogging(store) {
  2. const next = store.dispatch
  3. store.dispatch = function dispatchAndLog(action) {
  4. console.log('dispatching', action)
  5. let result = next(action)
  6. console.log('next state', store.getState())
  7. return result
  8. }
  9. }
  10.  
  11. function patchStoreToAddCrashReporting(store) {
  12. const next = store.dispatch
  13. store.dispatch = function dispatchAndReportErrors(action) {
  14. try {
  15. return next(action)
  16. } catch (err) {
  17. console.error('Caught an exception!', err)
  18. Raven.captureException(err, {
  19. extra: {
  20. action,
  21. state: store.getState()
  22. }
  23. })
  24. throw err
  25. }
  26. }
  27. }
  28.  
  29. patchStoreToAddLogging(store)
  30. patchStoreToAddCrashReporting(store)

但是这样并不好……很明显,我们在修改store的私有属性了,emmm……这是一个比较hack的方法……要改的优雅一点,把修改dispatch的部分封装起来。每一次返回新的dispatch,修改store的部分由 applyMiddlewareByMonkeypatching 统一处理。

  1. function logger(store) {
  2. const next = store.dispatch
  3. // Previously:
  4. // store.dispatch = function dispatchAndLog(action) {
  5. return function dispatchAndLog(action) {
  6. console.log('dispatching', action)
  7. let result = next(action)
  8. console.log('next state', store.getState())
  9. return result
  10. }
  11. }
  12.  
  13. function applyMiddlewareByMonkeypatching(store, middlewares) {
  14. middlewares = middlewares.slice()
  15. middlewares.reverse()
  16. // Transform dispatch function with each middleware.
  17. middlewares.forEach(middleware =>
  18. store.dispatch = middleware(store)
  19. )
  20. }
  21.  
  22. applyMiddlewareByMonkeypatching(store, [logger, crashReporter])

但是这样还是不太好。dispatch是store的私有属性,我们却直接获取了。思考我们为什么重写dispatch,因为我们在用多个中间件的时候,第一个中间件修改完dispatch,下一次修改应该是在前一个的基础之上,包裹上一次修改的dispatch。但其实,这也不是必要的,只要每一次传入上一次修改后的dispatch就可以了。

  1. function logger(store) {
  2. return function wrapDispatchToAddLogging(next) {
  3. return function dispatchAndLog(action) {
  4. console.log('dispatching', action)
  5. let result = next(action)
  6. console.log('next state', store.getState())
  7. return result
  8. }
  9. }
  10. }

这里的next就是之前的中间件处理后的dispatch,我们不再获取store的私有属性了,改为用参数传递。然后在处理之后(logger(store)(next))返回一个新的dispatch。

为什么这里要套两个函数而不是传入两个参数(store, next)呢,就相当于把这个函数柯里化了嘛……后面可以看到用处。

改成ES6的箭头函数

  1. const logger = store => next => action => {
  2. console.log('dispatching', action)
  3. let result = next(action)
  4. console.log('next state', store.getState())
  5. return result
  6. }

说实话虽然简洁了,但是看起来一点都不直观……可能是我太菜了。嗯,这就是一个中间件的写法了。

可以简单的实现下 applyMiddleware

  1. function applyMiddleware(store, middlewares) {
  2. middlewares = middlewares.slice()
  3. middlewares.reverse()
  4. let dispatch = store.dispatch
  5. middlewares.forEach(middleware =>
  6. dispatch = middleware(store)(dispatch)
  7. )
  8. return Object.assign({}, store, { dispatch })
  9. }

这样就可以最后使用 applyMiddleware

  1. import { createStore, combineReducers, applyMiddleware } from 'redux'
  2.  
  3. const todoApp = combineReducers(reducers)
  4. const store = createStore(
  5. todoApp,
  6. // applyMiddleware() tells createStore() how to handle middleware
  7. applyMiddleware(logger, crashReporter)
  8. )

深入(meiyou)的理解之后 开始看applyMiddleware.js源码

其中用到里 compose 要先看一下

compose.js

这个是函数式编程的一个……思想?应用?

将函数的嵌套调用写成组合  compose(b, c, a) 相当于   b(c(a(x)))

  1. export default function compose(...funcs) {
  2. if (funcs.length === 0) {
  3. return arg => arg
  4. }
  5.  
  6. if (funcs.length === 1) {
  7. return funcs[0]
  8. }
  9. // reduce的参数..
  10. // reduce(function(accumulator, currentValue, currentIndex, array) {...})
  11. return funcs.reduce((a, b) => (...args) => a(b(...args)))
  12. }
  13.  
  14. /********** 使用示例 **********/
  15.  
  16. let a = x => 'a' + x + 'a'
  17. let b = x => 'b' + x + 'b'
  18. let c = x => 'c' + x + 'c'
  19. let foo = compose(b, c, a)
  20. console.log(foo('v')) // bcavacb
  21. let bar = x => b(c(a(x)))
  22. console.log(bar('v')) // bcavacb

最后看applyMiddleware.js

  1. import compose from './compose'
  2.  
  3. /**
  4. * Creates a store enhancer that applies middleware to the dispatch method
  5. * of the Redux store. This is handy for a variety of tasks, such as expressing
  6. * asynchronous actions in a concise manner, or logging every action payload.
  7. *
  8. * See `redux-thunk` package as an example of the Redux middleware.
  9. *
  10. * Because middleware is potentially asynchronous, this should be the first
  11. * store enhancer in the composition chain.
  12. *
  13. * Note that each middleware will be given the `dispatch` and `getState` functions
  14. * as named arguments.
  15. *
  16. * @param {...Function} middlewares The middleware chain to be applied.
  17. * @returns {Function} A store enhancer applying the middleware.
  18. */
  19. export default function applyMiddleware(...middlewares) {
  20. return createStore => (...args) => {
  21. const store = createStore(...args)
  22. let dispatch = () => {
  23. throw new Error(
  24. `Dispatching while constructing your middleware is not allowed. ` +
  25. `Other middleware would not be applied to this dispatch.`
  26. )
  27. }
  28.  
  29. const middlewareAPI = {
  30. getState: store.getState,
  31. dispatch: (...args) => dispatch(...args)
  32. }
  33. const chain = middlewares.map(middleware => middleware(middlewareAPI))
  34. dispatch = compose(...chain)(store.dispatch)
  35.  
  36. return {
  37. ...store,
  38. dispatch
  39. }
  40. }
  41. }

applyMiddleware([middlewares]) 就是返回一个函数 传入createStore,返回新的createStore,创建的store的dispatch是经过中间件加工的。

这里可以看到编写中间件嵌套两个函数的用处,先传入一个store,只需要再传入一个最新的dispatch就可以了,就是把dispatch用中间件轮流处理一下。这里使用了compose。

勉强看完源码。假装自己理解了这样子。

Redux源码学习笔记的更多相关文章

  1. redux源码学习笔记 - createStore

    本篇是学习redux源码的一些记录,学习的redux版本是^4.0.1. 在页面开发时,需要管理很多状态(state),比如服务器响应,缓存数据,UI状态等等···当页面的庞大时,状态就会变的混乱.r ...

  2. redux源码学习笔记 - applyMiddleware

    在创建store时,createStore(reducer, preloadedState, enhancer),除了reducer函数,初始状态,还可以传入enhancer.这个enhancer在c ...

  3. redux源码学习笔记 - combineReducers

    上一篇有了解到,reducer函数的两个为:当前state和此次dispatch的action. state的结构是JavaScript对象,每个key都可以代表着不同意义的数据.比如说 { list ...

  4. Underscore.js 源码学习笔记(下)

    上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ...

  5. Underscore.js 源码学习笔记(上)

    版本 Underscore.js 1.9.1 一共 1693 行.注释我就删了,太长了… 整体是一个 (function() {...}());  这样的东西,我们应该知道这是一个 IIFE(立即执行 ...

  6. AXI_LITE源码学习笔记

    AXI_LITE源码学习笔记 1. axi_awready信号的产生 准备接收写地址信号 // Implement axi_awready generation // axi_awready is a ...

  7. Hadoop源码学习笔记(6)——从ls命令一路解剖

    Hadoop源码学习笔记(6) ——从ls命令一路解剖 Hadoop几个模块的程序我们大致有了点了解,现在我们得细看一下这个程序是如何处理命令的. 我们就从原头开始,然后一步步追查. 我们先选中ls命 ...

  8. Hadoop源码学习笔记(5) ——回顾DataNode和NameNode的类结构

    Hadoop源码学习笔记(5) ——回顾DataNode和NameNode的类结构 之前我们简要的看过了DataNode的main函数以及整个类的大至,现在结合前面我们研究的线程和RPC,则可以进一步 ...

  9. Hadoop源码学习笔记(4) ——Socket到RPC调用

    Hadoop源码学习笔记(4) ——Socket到RPC调用 Hadoop是一个分布式程序,分布在多台机器上运行,事必会涉及到网络编程.那这里如何让网络编程变得简单.透明的呢? 网络编程中,首先我们要 ...

随机推荐

  1. Android进阶:三、这一次,我们用最详细的方式解析Android消息机制的源码

    决定再写一次有关Handler的源码 Handler源码解析 一.创建Handler对象 使用handler最简单的方式:直接new一个Handler的对象 Handler handler = new ...

  2. pycharm的list中clear的应用

    #清空的意思 li = [11,22,33,44] li.clear() print(li) #输出[],就是把列表清空

  3. DWM1000 Blink结构 -- 帧过滤第一节

    DWM1000 帧结构分析主要学习DWM1000 帧过滤功能,希望在目前DS-TWR定位系统中增加中断和帧过滤功能,帧过滤功能可以有效减少系统中的各个模块同时收发数据时的干扰问题,从而极大的提供系统稳 ...

  4. MongoDB 学习使用

    博客教程: https://jingyan.baidu.com/article/dca1fa6f0428a4f1a440522e.html

  5. 无法运行 vue-manage-system@3.1.0 dev: `webpack-dev-server --inline --progress --

    一个项目的变大好多人开发,难免会有很多的冲突.每次跟新代码都要一个坑一个坑的解决的.这次遇到这个坑好大.急死了.... 百度了好多说占用端口,试了好几遍不行.最终还是要去查原因的....经过了几个小时 ...

  6. 对象转JSON

    /// <summary> /// 把对象序列化 JSON 字符串 /// </summary> /// <typeparam name="T"> ...

  7. [zt+总结]wpf 应用权限问题

    一.Inno Setup打包添加和去除管理员权限 转载:https://www.cnblogs.com/walker-lc/articles/3470679.html 添加管理员权限 1.在[Setu ...

  8. Python第一部分--Python简介+第一个程序+Python2和Python3介绍 001-016

    一.Python起源 1.1 解释器(科普) 1.2Python的设计目标 1.3 Python的设计哲学 02.为什么学Python? 代码量少 同一样问题,不用的语言解决,代码量差距还是很多的,一 ...

  9. 如何把if-else代码重构成高质量代码

    原文:https://blog.csdn.net/qq_35440678/article/details/77939999 本文提纲: 为什么我们写的代码都是if-else? 这样的代码有什么缺点? ...

  10. su;su -;sudo;sudo -i;sudo su;sudo su - 之间的区别

    今天我们来聊聊su;su -;sudo;sudo -i;sudo su;sudo su -他们之间的区别. su :su 在不加任何参数,默认为切换到root用户,但没有转到root用户家目录下,也就 ...