Redux源码解读 - bindActionCreators篇(完结)

Redux源码解读 - bindActionCreators篇(完结)

bindActionCreators

这个函数的主要作用,就是包装action的创建,并且和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
49
50
const reducer = (state = { val: 10 }, action) => {
switch (action.type) {
case "increment":
return {
...state,
val: state.val + action.val,
};
case "decrement":
return {
...state,
val: state.val - action.val,
};
default:
return state;
}
};

const store = createStore(reducer);

const incrementAction = (val) => {
return {
type: "increment",
val,
};
};

const decrementAction = (val) => {
return {
type: "decrement",
val,
};
};

// action创建函数和dispatch结合
const execAction = bindActionCreators(
{
incrementAction,
decrementAction,
},
store.dispatch
);

console.log(execAction);

store.subscribe(() => {
console.log(`当前state:${store.getState().val}`);
});

execAction.incrementAction(10);
execAction.decrementAction(5);

运行之后就可以看到效果:

通过包装,可以以一个函数来完成action的创建和分发。

在中文文档中,指出来这样做的好处(结合React)。

惟一会使用到bindActionCreators的场景是当你需要把action creator往下传到一个组件上,却不想让这个组件觉察到Redux的存在,而且不希望把dispatchRedux store传给它。

意思上大致是,对于一个纯组件,不应该明确地感知到Redux,通过bindActionCreators包装可以向纯组件传入一个函数来完成功能,而不是在纯组件中使用到dispatch函数和action对象。

ok,先贴上源码,其实这个函数并不难,比前面三个容易理解多了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 boundActionCreators = {};
for (const key in actionCreators) {
const actionCreator = actionCreators[key];
if (typeof actionCreator === "function") {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
}
}
return boundActionCreators;
}

这个函数支持两种传入的方式,一种是传入一个函数,一种是传入一个对象,这个对象的键的值为一个函数。

即在例子中的代码:

1
2
3
4
5
6
7
const execAction = bindActionCreators(
{
incrementAction,
decrementAction,
},
store.dispatch
);

可以写为

1
2
const execIncrementAction = bindActionCreators(incrementAction, store.dispatch);
const execDecrementAction = bindActionCreators(decrementAction, store.dispatch);

这两者的效果完全一样。

1
2
3
if (typeof actionCreators === "function") {
return bindActionCreator(actionCreators, dispatch);
}

函数开始的判断就是对应第一种情况,只传入一个函数。

可以看到直接返回了一个bindActionCreator的函数。

1
2
3
4
5
function bindActionCreator(actionCreator, dispatch) {
return function () {
return dispatch(actionCreator.apply(this, arguments));
};
}

这个函数也非常的简单,返回了一个函数,这个函数就是dispatch一个action

action参数通过传入的actionCreator函数来创建。

传入actionCreator的参数就是返回函数的参数列表。

也就是:

1
execAction.incrementAction(10); //这里的10就会传给actionCreator函数

接下来又进行了一次判断:

1
2
3
4
5
6
7
8
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"?`
);
}

如果不是object或者传了个空进来的话,直接报错。

1
2
3
4
5
6
7
const boundActionCreators = {};
for (const key in actionCreators) {
const actionCreator = actionCreators[key];
if (typeof actionCreator === "function") {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
}
}

最后这段代码就对应第二种情况,传入一个对象。

通过for-in循环,把值为函数的挑出来,然后使用第一种情况,存在一个boundActionCreators对象里。

最后返回boundActionCreators对象。

这个函数就是这么通俗易懂。

至此Redux的源码已经讲完啦。

个人感觉上还行,基本上都能搞懂。