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

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

// Web 应用是一个状态机,视图与状态是一一对应的
// 所有的状态,保存在一个对象里面 // store 是保存数据的地方 // 创建 store
import { createStore } from 'redux'
const store = createStore(fn) // state 是某一时刻 store 的快照,一个 state 对应一个 view
// 可通过 getState() 获取
const state = store.getState() // Action 是一个对象 用来表示 view 发出的改变 state 的通知
// type 是必须的 其他属性可以自由设置
const action = {
type: 'ADD_TODO',
payload: 'Learn Redux'
} // 同一种类型的 action 可以写一个函数生成
const ADD_TODO = '添加 TODO'
// 生成 action 的函数: Action Creator
function addTodo(text) {
return {
type: ADD_TODO,
text
}
} const action = addTodo('Learn Redux') // store.dispatch()是 View 发出 Action 的唯一方法。
store.dispatch(action) // reducer 是 store 接收 state 返回新的 state 的过程 const defaultState = 0
// reducer 接收 action 返回新的 state
const reducer = (state = defaultState, action) => {
switch(action.type) {
case: 'ADD':
return state + action.payload
default:
return state
}
}
const state = reducer(1, {
type: 'ADD',
payload: 2
}) // 创建 store 时传入 reducer 当调用 store.dispatch 时将自动调用 reducer
const store = createStore(reducer) /*
reducer 是一个纯函数,纯函数要求:
- 不得改写参数
- 不能调用系统 I/O 的API
- 不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果
*/ // store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数
// 返回解除监听函数
let unsubscribe = store.subsribe(() => { console.log(store.getState) })
unsubscribe() // 解除监听 /*
store 提供的三个方法
- store.getState()
- store.dispatch()
- store.subscribe()
*/ // createStore方法还可以接受第二个参数,表示 State 的最初状态。这通常是服务器给出的。
// !这个初始值会覆盖 Reducer 函数默认的初始值
let store = createStore(todoApp, STATE_FROM_SERVER) // createStore 的简单实现
const createStore = (reducer) => {
let state
let listeners = [] const getState = () => state const dispatch = action => {
state = reducer(state, action)
listeners.forEach(listener => listener())
} const subscribe = listener => {
listeners.push(listener)
return () => {
listeners = listeners.filter(l => l !== listener)
}
} dispatch({}) return { getState, dispatch, subscribe } } // 可以通过 combineReducers 来将多个 Reducer 合为一个
import { combineReducers } from 'redux' const chatReducer = combineReducers({
chatLog,
statusMessage,
userName
}) // combineReducer 的简单实现
const combineReducers = reducers => {
return (state = {}, action) =>
Object.keys(reducers).reduce(
(nextState, key) => {
nextState[key] = reducers[key](state[key], action)
return nextState
},
{}
)
}

工作流程

Redux Flow

        dispatch(action)   (previousState, action)
Action Creators ======> Store ======> Reducers
^ || <======
\_ || (newState)
\_ (state) ||
\_ ||
(view opt)\_ \/
\--- React Comonents

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

combineReducers.js

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

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

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

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

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

function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i] if (typeof reducers[key] === 'function') {
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) nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
// 如果state每一个key都没有被修改 就直接返回原state
return hasChanged ? nextState : state
}
} /***************** 下面是简单的用法实例 *****************/
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat(action.text)
default:
return state
}
} function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
} let reducer = combineReducers({ list: todos, number: counter })
let state = { list: [], number: 0, otherKey: 'no reducer match will be ignore' }
console.log(state) // { list: [], number: 0, otherKey: 'no reducer match will be ignore' }
state = reducer(state, { type: 'ADD_TODO', text: 'study' })
console.log(state) // { list: [ 'study' ], number: 0 }
state = reducer(state, { type: 'ADD_TODO', text: 'sleep' })
console.log(state) // { list: [ 'study', 'sleep' ], number: 0 }
state = reducer(state, { type: 'INCREMENT' })
console.log(state) // { list: [ 'study', 'sleep' ], number: 1 }

combineReducers.js 源码

import ActionTypes from './utils/actionTypes'
import warning from './utils/warning'
import isPlainObject from './utils/isPlainObject' function getUndefinedStateErrorMessage(key, action) {
const actionType = action && action.type
const actionDescription =
(actionType && `action "${String(actionType)}"`) || 'an action' return (
`Given ${actionDescription}, reducer "${key}" returned undefined. ` +
`To ignore an action, you must explicitly return the previous state. ` +
`If you want this reducer to hold no value, you can return null instead of undefined.`
)
} function getUnexpectedStateShapeWarningMessage(
inputState,
reducers,
action,
unexpectedKeyCache
) {
const reducerKeys = Object.keys(reducers)
const argumentName =
action && action.type === ActionTypes.INIT
? 'preloadedState argument passed to createStore'
: 'previous state received by the reducer' if (reducerKeys.length === 0) {
return (
'Store does not have a valid reducer. Make sure the argument passed ' +
'to combineReducers is an object whose values are reducers.'
)
} if (!isPlainObject(inputState)) {
// 希望 inputState 是一个简单对象:通过 new Object() 、 {} 创建 (Object.create(null) 这里好像是不合法的
// [object Array] 中提取 'Array'
// Object.prototype.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1]
return (
`The ${argumentName} has unexpected type of "` +
{}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
`". Expected argument to be an object with the following ` +
`keys: "${reducerKeys.join('", "')}"`
)
}
// 检查所有Reducer都没有处理到的key ( 此处实在不解 unexpectedKeyCache 到底何用= =
const unexpectedKeys = Object.keys(inputState).filter(
key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]
) unexpectedKeys.forEach(key => {
unexpectedKeyCache[key] = true
})
// 替换 store 的 Reducer 时会调用 dispatch({ type: ActionTypes.REPLACE })
if (action && action.type === ActionTypes.REPLACE) return if (unexpectedKeys.length > 0) {
return (
`Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` +
`"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` +
`Expected to find one of the known reducer keys instead: ` +
`"${reducerKeys.join('", "')}". Unexpected keys will be ignored.`
)
}
} function assertReducerShape(reducers) {
Object.keys(reducers).forEach(key => {
const reducer = reducers[key]
const initialState = reducer(undefined, { type: ActionTypes.INIT })
// Reducer"$ {key}"在初始化时返回undefined。如果传递给reducer的状态未定义,你必须明确返回初始状态。
// 初始状态可以是不可定义。如果你不想为这个reducer设置一个值,你可以使用null而不是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. If you don't want to set a value for this reducer, ` +
`you can use null instead of undefined.`
)
} if (
typeof reducer(undefined, {
type: ActionTypes.PROBE_UNKNOWN_ACTION()
}) === 'undefined'
) {
// 当使用随机类型探测Reducer${key}时返回undefined。
// 不要试图处理${ActionTypes.INIT}或者其他在"redux/*"命名空间的动作。它们被认为是私有的。
// 相反,当你遇到任何未知动作时,你必须返回当前的state,除非当前state是undefined,
// 那样你要返回初始状态,而不管动作类型。初始状态不可以是undefined,但可以为null
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, but can be null.`
)
}
})
} /**
* Turns an object whose values are different reducer functions, into a single
* reducer function. It will call every child reducer, and gather their results
* into a single state object, whose keys correspond to the keys of the passed
* reducer functions.
*
* @param {Object} reducers An object whose values correspond to different
* reducer functions that need to be combined into one. One handy way to obtain
* it is to use ES6 `import * as reducers` syntax. The reducers may never return
* undefined for any action. Instead, they should return their initial state
* if the state passed to them was undefined, and the current state for any
* unrecognized action.
*
* @returns {Function} A reducer function that invokes every reducer inside the
* passed object, and builds a state object with the same shape.
*/
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') {
warning(`No reducer provided for key "${key}"`)
}
} if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers) let unexpectedKeyCache
if (process.env.NODE_ENV !== 'production') {
unexpectedKeyCache = {}
} let shapeAssertionError
try {
// 判断每个reducer都有初始值和对于未知action返回原state
assertReducerShape(finalReducers)
} catch (e) {
shapeAssertionError = e
} return function combination(state = {}, action) {
if (shapeAssertionError) {
throw shapeAssertionError
} if (process.env.NODE_ENV !== 'production') {
const warningMessage = getUnexpectedStateShapeWarningMessage(
state,
finalReducers,
action,
unexpectedKeyCache
)
if (warningMessage) {
warning(warningMessage)
}
} 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
}
// 如果state每一个key都没有被修改 就直接返回原state
return hasChanged ? nextState : state
}
}

utils/actionTypes.js

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

createStore.js 

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

import $$observable from 'symbol-observable'

import ActionTypes from './utils/actionTypes'
import isPlainObject from './utils/isPlainObject' // 创建 store 的函数
// preloadedState: store设置的初始值 这个值会覆盖 Reducer 的默认值
// 如果使用了 combineReducers preloadedState 要和 combineReducers 有相同的keys
// enhancer: 中间件
export default function createStore(reducer, preloadedState, enhancer) {
// preloadedState可以不传 判断preloadedState是否存在
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.')
}
// enhancer是一个高阶函数 调用enhancer返回一个"加强版"的createStore
return enhancer(createStore)(reducer, preloadedState)
} if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
} let currentReducer = reducer
let currentState = preloadedState
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false
// 判断当前 nextListeners 和 currentListeners 是否为同一个对象
// 如果是一个对象 就把 nextListeners 改为 currentListeners 的副本
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
// 获取当前对象 如果是正在派发action 则不能获取state
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
}
// 订阅 添加订阅者
function subscribe(listener) {
if (typeof listener !== 'function') {
throw new Error('Expected the listener to be a function.')
} if (isDispatching) {
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 = true
// 每次修改 nextListeners 都要判断一下 nextListeners 和 currentListeners 是否为同一个对象
ensureCanMutateNextListeners()
// 注意 这里修改 nextListeners 之后并没有改变 currentListeners 而是在下一次用到 currentListeners 才会改变
nextListeners.push(listener) // 返回一个当前监听者取消订阅的方法
return function unsubscribe() {
if (!isSubscribed) {
return
}
// 正在派发 action 时不能进行操作
if (isDispatching) {
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 = false ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
} 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?'
)
} if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
} try {
// 用 isDispatching 记录是否正在 派发action 过程中不能进行其他操作
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
// 用到 listeners 才会修改 currentListeners 以减少修改次数
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
} return action
} // 替换 Reducer 并派发动作 ActionTypes.REPLACE 相当于对state重新进行初始化
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.')
} currentReducer = nextReducer
dispatch({ type: ActionTypes.REPLACE })
}
// emmmm...看不懂这个 可以参考 https://distums.github.io/2017/03/19/observables-proposal-for-ecmascript/
function observable() {
const outerSubscribe = subscribe
return {
subscribe(observer) {
if (typeof observer !== 'object' || observer === null) {
throw new TypeError('Expected the observer to be an object.')
} function observeState() {
if (observer.next) {
observer.next(getState())
}
} observeState()
const unsubscribe = outerSubscribe(observeState)
return { unsubscribe }
}, [$$observable]() {
return this
}
}
} dispatch({ type: ActionTypes.INIT }) return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}

bindActionCreators.js

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

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

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

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

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

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

例:

action.increase = (info) => { type:'INCREASE',info }
action.decrease = (info) => { type:'DECREASE',info } bindActionCreators({
increase: action.increase,
decrease: action.decrease
}, dispatch) // 就可以获得:
{
increase: (...args) => dispatch(action.increase(...args)),
decrease: (...args) => dispatch(action.decrease(...args))
}

源码:

function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
} /**
* Turns an object whose values are action creators, into an object with the
* same keys, but with every function wrapped into a `dispatch` call so they
* may be invoked directly. This is just a convenience method, as you can call
* `store.dispatch(MyActionCreators.doSomething())` yourself just fine.
*
* For convenience, you can also pass a single function as the first argument,
* and get a function in return.
*
* @param {Function|Object} actionCreators An object whose values are action
* creator functions. One handy way to obtain it is to use ES6 `import * as`
* syntax. You may also pass a single function.
*
* @param {Function} dispatch The `dispatch` function available on your Redux
* store.
*
* @returns {Function|Object} The object mimicking the original object, but with
* every action creator wrapped into the `dispatch` call. If you passed a
* function as `actionCreators`, the return value will also be a single
* function.
*/
export default function bindActionCreators(actionCreators, dispatch) {
// 如果 actionCreators 是一个函数 说明只有一个 actionCreator
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
} if (typeof actionCreators !== 'object' || actionCreators === null) {
throw new Error(
`bindActionCreators expected an object or a function, instead received ${
actionCreators === null ? 'null' : typeof actionCreators
}. ` +
`Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
)
} const keys = Object.keys(actionCreators)
const boundActionCreators = {}
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}

applyMiddleware.js

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

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

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

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

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

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

const action = addTodo('Use Redux')

console.log('dispatching', action)
store.dispatch(action)
console.log('next state', store.getState())

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

const next = store.dispatch
store.dispatch = function dispatchAndLog(action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}

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

function patchStoreToAddLogging(store) {
const next = store.dispatch
store.dispatch = function dispatchAndLog(action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
} function patchStoreToAddCrashReporting(store) {
const next = store.dispatch
store.dispatch = function dispatchAndReportErrors(action) {
try {
return next(action)
} catch (err) {
console.error('Caught an exception!', err)
Raven.captureException(err, {
extra: {
action,
state: store.getState()
}
})
throw err
}
}
} patchStoreToAddLogging(store)
patchStoreToAddCrashReporting(store)

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

function logger(store) {
const next = store.dispatch
// Previously:
// store.dispatch = function dispatchAndLog(action) {
return function dispatchAndLog(action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
} function applyMiddlewareByMonkeypatching(store, middlewares) {
middlewares = middlewares.slice()
middlewares.reverse()
// Transform dispatch function with each middleware.
middlewares.forEach(middleware =>
store.dispatch = middleware(store)
)
} applyMiddlewareByMonkeypatching(store, [logger, crashReporter])

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

function logger(store) {
return function wrapDispatchToAddLogging(next) {
return function dispatchAndLog(action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
}
}

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

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

改成ES6的箭头函数

const logger = store => next => action => {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}

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

可以简单的实现下 applyMiddleware

function applyMiddleware(store, middlewares) {
middlewares = middlewares.slice()
middlewares.reverse()
let dispatch = store.dispatch
middlewares.forEach(middleware =>
dispatch = middleware(store)(dispatch)
)
return Object.assign({}, store, { dispatch })
}

这样就可以最后使用 applyMiddleware

import { createStore, combineReducers, applyMiddleware } from 'redux'

const todoApp = combineReducers(reducers)
const store = createStore(
todoApp,
// applyMiddleware() tells createStore() how to handle middleware
applyMiddleware(logger, crashReporter)
)

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

其中用到里 compose 要先看一下

compose.js

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

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

export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
} if (funcs.length === 1) {
return funcs[0]
}
// reduce的参数..
// reduce(function(accumulator, currentValue, currentIndex, array) {...})
return funcs.reduce((a, b) => (...args) => a(b(...args)))
} /********** 使用示例 **********/ let a = x => 'a' + x + 'a'
let b = x => 'b' + x + 'b'
let c = x => 'c' + x + 'c'
let foo = compose(b, c, a)
console.log(foo('v')) // bcavacb
let bar = x => b(c(a(x)))
console.log(bar('v')) // bcavacb

最后看applyMiddleware.js

import compose from './compose'

/**
* Creates a store enhancer that applies middleware to the dispatch method
* of the Redux store. This is handy for a variety of tasks, such as expressing
* asynchronous actions in a concise manner, or logging every action payload.
*
* See `redux-thunk` package as an example of the Redux middleware.
*
* Because middleware is potentially asynchronous, this should be the first
* store enhancer in the composition chain.
*
* Note that each middleware will be given the `dispatch` and `getState` functions
* as named arguments.
*
* @param {...Function} middlewares The middleware chain to be applied.
* @returns {Function} A store enhancer applying the middleware.
*/
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let 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))
dispatch = compose(...chain)(store.dispatch) return {
...store,
dispatch
}
}
}

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. 将已经存在的异步请求callback转换为同步promise

    由于js是单线程执行,为防止阻塞,会有很多异步回调函数callback,嵌套层次多了,可读性就差了很多.随着社区的发展,出现了promise.我们来将一些常见的回调函数做修改,变成promise的链式 ...

  2. [FJWC2018]全排列

    题解: 考虑长度为k的时候的贡献 即取出一些元素然后给他们排个顺序然后问你有多少排法 假设排法为ans 那么应该就是$C(n,k)*C(n,k)*(n-k)!*(n-k)!*(n-k+1)*ans$ ...

  3. C++11 带来的新特性 (4)—— 匿名函数(Lambdas)

    1 语法 Lambdas并不是新概念,在其它语言中已经烂大街了.直接进入主题,先看语法: [ captures ] ( params ) specifiers exception attr -> ...

  4. LOJ#3043.【ZJOI2019】 线段树 线段树,概率期望

    原文链接www.cnblogs.com/zhouzhendong/p/ZJOI2019Day1T2.html 前言 在LOJ交了一下我的代码,发现它比选手机快将近 4 倍. 题解 对于线段树上每一个节 ...

  5. Docker入门到实践

    1.什么是Docke 1.网上有很多 2.为什么要使用Docker? 优点 更高效的利用系统资源 更快速的启动时间 一致的运行环境 持续交付和部署 更轻松的迁移 更轻松的维护和扩展 3.Docker的 ...

  6. 阿里云服务器 yii2执行composer提示报错

    未解决 composer installLoading composer repositories with package informationUpdating dependencies (inc ...

  7. 【面试题】Java实现String的IndexOf方法

    先说题后感:程序员这一行,很多时候,自驱学习能力是自我成长一个很重要的因素(当然技术最好的学习途径都是通过项目实践去学习.理解.掌握).而自学方法中,除了看官方文档.技术博客等途径之外,学习源码也是一 ...

  8. System.getProperty(String key)方法获取常用系统信息

    其中key可以为以下选项: 1.java.version Java 运行时环境版本 2.java.vendor Java 运行时环境供应商 3.java.vendor.url Java 供应商的 UR ...

  9. 原生js的联动全选

    开发应用中有很多工具可以使用,下面介绍一个原生js写的联动全选思路!!! <!DOCTYPE html> <html lang="en"> <head ...

  10. IIS 程序池与Site 导出、导入

    如何在IIS7或IIS7.5中导入导出站点及应用程序池. 为实现负载平衡,我们可能会使用多个WEB服务器,也就会需要给多个IIS配置同样的站点和应用程序池.那么我们需要一个一个的重新建吗?当然不用,我 ...