问:你了解thunk吗?
答:你第一次听说redux-thunk
时头皮发麻的声音。
抱歉,这样的回答实在是有点糟糕。
但是,讲真的:如果你之前没有接触过,Redux Thunk 绝对是个令人迷惑的东西。我觉得大部分原因在于 thunk
这个单词吧。那么让我们先把这个单词的意思搞明白吧。
thunk,名词
thunk 是函数(function)的另一种表达方式。但它并不仅仅是传统的函数,而是由其他函数返回的一种特殊且不常见的函数的别称。比如下面这个:
function wrapper_function(){
// 这个函数就称之为 thunk,它的功能是将任务延迟执行
return function thunk(){ // 可以是具名函数,也可以匿名
console.log('do stuff now')
}
}
其实你已经了解过或用过这种模式。只不过你不知道他就叫thunk
罢了。如果你想打印do stuff now
,只需要执行两次wrapper_function
就可以了,即wrapper_function()()
。
redux-thunk
那么,这种形式的函数又是如何应用到Redux中去的呢?
如果你对Redux熟悉的话,你会知道其中有几个重要的概念:actions
、action creators
、reducers
和middleware
。
actions
即普通的对象。就Redux而言,开箱即用的actions
必须是普通的对象,而且其必须含有一个type
属性。除了上述要求,你可以在这个对象中描述任何你需要执行的action
。actions
形式如下:
// 1. 普通对象
// 2. 有一个type属性
// 3. 任何其他你需要的
{
type: "USER_LOGGED_IN",
username: "dave"
}
由于一直重复的去写这些对象很烦人,于是 Redux 就有了action-creators
的概念。
function userLoggedIn() {
return {
type: 'USER_LOGGED_IN',
username: 'dave'
};
}
这个虽然看起来跟前面的action
是一样的,但现在方便的是你可以通过调用userLoggedIn
函数来生成action
了。这样,就对其进行了一次抽象。
现在你可以通过调用函数来创建actions
返回对象了,再也不用你去手敲了。这时如果你需要在你的项目中 dispatch 多次相同的action
,action creators
就能帮你省很多力气了。
Actions太枯燥了
现在你有没有发现一个有趣的事情,Redux所谓的actions
实际上啥都没干。它们就是对象而已,普通、简单又没什么用武之地。
那么如果你真的可以让它们做点什么,那不是很酷吗?比方说,调用一个API,或是触发其他操作?
由于reducers
应该是纯函数(不改变任何作用域外的东西),所以我们并不能在一个 reducer 内部调用任何API或者是 dispatch 一个 action
。
如果你想要让一个action
去做点儿什么,那么你的代码应该包含在一个函数内部。这个函数(也即 thunk)是一系列将来才会完成的操作。
要是action creators
可以完成这样的功能那就太棒了。它会返回我们需要的一系列将来会被执行的动作,而不是简单的对象。比如下面这样:
function getUser() {
return function() {
return axios.get('/current_user');
};
}
现在好了,redux-thunk
确实就是干这个的。它就是个中间件,去监控传入系统中的每一个action
,如果是个函数的话,那么它就会调用那个函数。这就是redux-thunk
的职责。
在上面的示例中,我唯一漏掉的一点就是Redux会传递两个参数给thunk函数:dispatch
- 如果需要的话可以 dispatch 新的 action
;getState
- 用于访问当前 state。
function logOutUser() {
return function(dispatch, getState) {
return axios.post('/logout').then(function() {
// 假设我们声明过一个 action creator
// 叫做 'userLoggedOut', 现在我们可以 dispatch 它了
dispatch(userLoggedOut());
});
};
}
再补充一点:getState
还可用于决定是获取新数据还是返回缓存结果,这取决于当前 state。
这就是redux-thunk
做的事情了。
着实是个很小的库了
redux-thunk
库的完整源码都在这了:
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
// 在这儿调用所有你 dispatch 的 action
// 如果是个函数的话,直接调用
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
// 否则,继续处理该action
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
在你项目中安装了redux-thunk
后,你 dispatch 的每一个 action 都会经过这几行代码的处理。它调用actions
为函数的actions
(不去管它返回什么),如果actions
不是函数,就将其传给下一个中间件或者Redux本身(也就是next(action)
这行代码所做的工作)。
在你的项目中使用 redux-thunk
如果你的项目中已经配置好了 Redux,那么添加redux-thunk
只需两步:
首先,安装redux-thunk
:
npm install --save redux-thunk
然后,不论你的Redux配置代码在哪儿,你只需引入redux-thunk
后把该中间件插入到Redux当中去:
// 你需要引入 appleMiddleware
import { createStore, applyMiddleware } from 'redux';
// 引入 thunk 中间件
import thunk from 'redux-thunk';
// 引入现存的根reducer路径
// 改变该路径以适应你的配置
import rootReducer from './reducers/index';
// createStore 的最后一个参数为「增强store」,
// 这里我们基于 thunk 中间件,使用applyMiddleware来创建该「增强store」
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
只需确保你正确的调用applyMiddleware
时传入了 thunk,否则它就不能起作用了。