redux源码 | Part3_combineReducers && bindActionCreators

学习资料

redux源码 v 4.x

学习目标

  1. redux源码createStore(Part1)
  2. redux中间件原理(Part2)
  3. redux源码combineReducers(Part3)

过程解析

getUndefinedStateErrorMessage

1
2
3
4
5
6
7
8
9
10
11
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.`
)
}

getUnexpectedStateShapeWarningMessage

getUnexpectedStateShapeWarningMessage函数:一些错误性的处理

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
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)) {
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('", "')}"`
)
}

const unexpectedKeys = Object.keys(inputState).filter(
key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]
)

unexpectedKeys.forEach(key => {
unexpectedKeyCache[key] = true
})

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.`
)
}
}

assertReducerShape(reducers)

assertReducerShape函数:

​ 这部分函数其实就是对reducer的一个校验

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
/*
assertReducerShape(reducers)函数
输入值分析:
- reducers
输出分析:
- reducers
*/
function assertReducerShape(reducers) {
Object.keys(reducers).forEach(key => {
const reducer = reducers[key]
const initialState = reducer(undefined, { type: ActionTypes.INIT })

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'
) {
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.`
)
}
})
}

combineReducers(reducers)

combineReducers函数:用于合并reducers

基本思路:

  • reducers浅复制到finalReducers里面
  • reducers基本校验
  • 返回combination函数

combination函数:

基本思路:

  • 基本性的检测
  • 维护两个变量hasChanged = false[用于监听状态有没有改变]和nextState = {}[用于存更新后的状态]
  • 遍历finalReducers,获取reducer和state里面存储的reducer所需要的state
    • 通过reducer(state,action)得到新的状态
    • 得到新的状态存入nextState里面
    • 判断是否状态改变
  • 最终判断状态改变否
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
117
118
119
/*
combineReducers函数用于合并reducers
输入参数说明:
- reducers:多个reducer
输出说明:
- combination 函数 (后面具体分析)
*/
export default function combineReducers(reducers) {
/*
mock:
reducers = {
reducer1: reducer1,
reducer2: reducer2,
...
reducern: reducern,
}
维护两个变量:
reducerKeys = [reducer1, reducer2, ..., reducer3]
finalReducers = {}
*/
const reducerKeys = Object.keys(reducers)
const finalReducers = {}

// 以下是浅复制的过程... 得到finalReducers
// 遍历reducers
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]

// process.env.NODE_ENV用于判断生产环境或开发环境
if (process.env.NODE_ENV !== 'production') {
if (typeof reducers[key] === 'undefined') {
warning(`No reducer provided for key "${key}"`)
}
}

// 如果reducer是一个函数,就添加到finalReducers里面
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}

const finalReducerKeys = Object.keys(finalReducers)

// This is used to make sure we don't warn about the same
// keys multiple times.
let unexpectedKeyCache
if (process.env.NODE_ENV !== 'production') {
unexpectedKeyCache = {}
}

// 校验reducers中的reducer
let shapeAssertionError
try {
assertReducerShape(finalReducers) // --------往上翻这个函数做了什么
} catch (e) {
shapeAssertionError = e
}


/*
combination函数:
输入参数分析:
- state:初始值为空对象
- action:一个对象 {type:xxx, data:xxx}
*/
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)
}
}

/*
维护两个变量:
hasChanged = false
nextState = {}
*/
let hasChanged = false // 用于监听状态有没有改变的
const nextState = {}
/*
mock:
finalReducers = {
reducer1: reducer1,
reducer2: reducer2,
...
reducern: reducern,
}

finalReducerKeys = [reducer1, reducer2, reducer3, ...]

*/
for (let i = 0; i < finalReducerKeys.length; i++) { // 遍历finalReducers
const key = finalReducerKeys[i] // reducer1, reducer2, reducer3, ...
const reducer = finalReducers[key] // 获取当前reducer: reducer1, reducer2, reducer3
const previousStateForKey = state[key] // 获取当前reducer在state
const nextStateForKey = reducer(previousStateForKey, action) // reducer纯函数加工状态获得新的state
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action) // --------往上翻这个函数做了什么
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey // nextState里面存入新的state [key=key, value=新的state]
hasChanged = hasChanged || nextStateForKey !== previousStateForKey // 小状态改变否???
}

hasChanged =
hasChanged || finalReducerKeys.length !== Object.keys(state).length // 整体状态改变否?
return hasChanged ? nextState : state // 改变了 返回nextState 否则 返回state
}
}

bindActionCreators(actionCreator, dispatch)

bindActionCreator函数:

将一个或多个action和dispatch组合起来生成mapDispatchToProps需要生成的内容

connect(mapStateToProps, mapDispatchToProps)(UI component) connect用以从UI组件生成容器组件

mapStateToProps:将state映射到UI组件的参数(props)

mapDispatchToProps:将用户对UI组件的操作映射成Action

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
function bindActionCreator(actionCreator, dispatch) {
return function() {
/*
apply修改this指向
*/
return dispatch(actionCreator.apply(this, arguments))
}
}

export default function bindActionCreators(actionCreators, dispatch) {
// 参数校验
/*
如果actionCreators是一个函数,那么就需要bindActionCreator进行一些处理
并且看到bindActionCreator返回的是一个函数

如果actionCreators不是一个object(排除null),报错!
*/
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"?`
)
}

// 维护一个参数boundActionCreators是一个对象,初始为空对象
const boundActionCreators = {}

/*
遍历actionCreators,如果actionCreator是一个函数,bindActionCreator后返回一个函数
最终返回boundActionCreators对象,key为原来的key,value为函数形式
*/
for (const key in actionCreators) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}