Redux源码解析06--bindActionCreators

bindActionCreators

单个的封装:bindActionCreator(actionCreator)

1
2
3
4
5
6
7
8
9
10
11
/**
* 对单个actionCreator的dispatch封装
* @param actionCreator
* @param dispatch
* @return {function(): *}
*/
function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}

对Object的封装:bindActionCreators(actionCreatorObject)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* 对一个对象上的每个actionCreator进行封装,使得其在调用时可以一并生成action和传入dispatch
* 这个行为等同于一步完成`store.dispatch(MyActionCreators.doSomething())`的方案,是一个更方便的封装形式
* @param actionCreators
* @param dispatch
* @return {*}
*/
export default function bindActionCreators(actionCreators, dispatch) {
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 = {}
/**
* 遍历处理每个actionCreator
*/
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
}

Redux源码解析05--combineReducers(reducers)

combineReducers(reducers)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/**
* 将自身各个属性为reducer的对象转换为一个reducer函数。
* 当它被调用时,同时也会调用它的子reducer,并将它们的结果分别收集起来,放入对应属性下。
* 这个属性名是它们被传入时所使用的属性名。
* 比如:
* combineReducers({
* moduleA: reducerA,
* moduleB: reducerB,
* })
* 执行后,这部分的state树:
* {
* moduleA: reducerA(state.moduleA, action),
* moduleB: reducerB(state.moduleB, action),
* }
* state为当前这个combine后的reducer传入的state,可能是上层state树的一部分(对应属性下的内容)
*
* @param reducers
* @return {function(*=, *=): *}
*/
export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
/**
* 遍历传入对象的所有属性
* 检查各个属性是否为{string}: {Function}形式的键值对,提取出合法的键值对
*/
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 {
assertReducerShape(finalReducers)
} catch (e) {
shapeAssertionError = e
}

/**
* 回传改造后的reducer
*/
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 = {}
/**
* 对自身的子reducer遍历传参执行
*/
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
/**
* 取出对应reducer
*/
const reducer = finalReducers[key]
/**
* 记录当前reducer执行前的,自身管理的state
*/
const previousStateForKey = state[key]
/**
* 获得当前reducer执行后的,自身管理的state
* 如果得到了undefined,要提示错误
*/
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}

/**
* 没有错误的情况下,就加入父级的nextState
*/
nextState[key] = nextStateForKey
/**
* 检查有没有发生改变,一旦有一处改变,即认定父state树发生了改变
*/
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
/**
* 如果没有发生改变,就不需要返回一个新的对象
*/
return hasChanged ? nextState : state
}
}

Redux源码解析04--applyMiddleware

applyMiddleware

middleware要分三次执行,才能获得最终的效果。

  • 第一次传入middlewareAPI,即可以访问getState()dispatch()的store
  • 第二次传入next,是对其内层middleware闭包的引用
  • 第三次传入action,并且使用next(action)的形式来传递action

一个典型的middleware的形式为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 一个典型的Middleware的形式
* store其实是改造过的middlewareAPI
* next表示内部一层的middleware,用来移交控制权。最内层的是dispatch
* action在运行时被传入,传递真正的action
*/
export const crashReporter = store => next => action => {
try {
return next(action)
} catch (err) {
if (process.env.NODE_ENV !== 'production') {
console.log('Caught an exception in Redux', err)
}
}
}

applyMiddleware将多个middleware加入dispatch的流程中,在其之前和之后都进行一次拦截处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* 创建一个enhancer,将middleware的应用于dispatch方法。
* 由于middleware可能是异步的,所以这个方法应当在最前面使用。
* 每个dispatch都能在第一个传入的参数(middlewareAPI)中访问dispatch和getState
* @param middlewares
* @return {function(*): function(...[*]): {dispatch: dispatch}}
*/
export default function applyMiddleware(...middlewares) {
// 先传入createStore,后面再传入reducer和preloadedState
return createStore => (...args) => {
// 创建store
const store = createStore(...args)

// 不允许在构建middleware时调用dispatch
let dispatch = () => {
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
)
}

/**
* 构建middlewareAPI,替代store
*/
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
/**
* 给每个middleware传入第一个参数store(使用middlewareAPI,而不是暴露整个store实例)
*/
const chain = middlewares.map(middleware => middleware(middlewareAPI))

/**
* 将传入store的middleware互相嵌套起来,并传入第二个参数next
* 最内层传入dispatch,从最内层开始运行,一级一级向外返回闭包,成为外层的第二个参数next
*/
dispatch = compose(...chain)(store.dispatch)

/**
* 等到进行真正的dispatch的时候,传入第三个参数action
*/
return {
...store,
dispatch
}
}
}