我们先来复习一下 redux 的 middleware
,以及 applyMiddleware
对中间件进行的串联处理。
源码解读:
// 定义一个代码组合的方法
// 传入一些function作为参数,返回其链式调用的形态。例如,
// compose(f, g, h) 最终返回 (...args) => f(g(h(...args)))
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
} else {
const last = funcs[funcs.length - 1]
const rest = funcs.slice(0, -1)
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}
}
export default function applyMiddleware(...middlewares) {
// 最终返回一个以createStore为参数的匿名函数
// 这个函数返回另一个以reducer, initialState, enhancer为参数的匿名函数
return (createStore) => (reducer, initialState, enhancer) => {
var store = createStore(reducer, initialState, enhancer)
var dispatch
var chain = []
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
// 每个 middleware 都以 middlewareAPI 作为参数进行注入调用,返回一个新的链。此时的返回值相当于调用 thunkMiddleware 返回的函数: (next) => (action) => {} ,接收一个next作为其参数
chain = middlewares.map(middleware => middleware(middlewareAPI))
// 并将链代入进 compose 组成一个函数的调用链
// compose(...chain) 返回形如(...args) => f(g(h(...args))),f/g/h都是chain中的函数对象。
// 之后以 store.dispatch 作为参数进行注入
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
在 applyMiddleware
内部,传入的所有 middleware 通过 map
进行遍历,每个 middleware 都以 { getState: store.getState, dispatch: (action) => dispatch(action) }
为参数进行一次调用。在这之后,通过 compose
方法进行代码组合:
// 传入一些function作为参数,返回其链式调用的形态。例如,
// compose(f, g, h) 最终返回 (...args) => f(g(h(...args)))
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
} else {
const last = funcs[funcs.length - 1]
const rest = funcs.slice(0, -1)
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}
}
所有的 middleware 形成一个层层包含的调用链。然后再以 store.dispatch
为参数进行调用。每一层 middleware 的调用,都会返回一个新的、被包裹封装了的 dispatch 函数(在每一层的封装里,middleware 可以调用到 action
、getState
API,以此做到自己想做的事情 ),最终返回一个新的 dispatch
:
dispatch = compose(...chain)(store.dispatch) // 这个 dispatch 作为最终 redux 应用的 dispatch 函数
由此可见,在通过 applyMiddleware
封装了 middleware 之后,每次 dispatch(someAction)
,会以此把 action 传入各个 middleware 中,middleware 可以做自己想做的操作,比如输出 log,调用 promise 等。
由上可见,middleware 的规范是有一定限制。
const middleware = ({ dispatch, getState }) =>
(next) => (action) => {
// do something
return next(action);
}
next
其实就是被改变过的 dispatch
函数。在每个 middleware 中,可以收到用户分发的 action
函数;进行完自己的操作以后,需要通过 next
继续将 action
分发下去。
因此,你可以自己尝试写一个简单的 输出 log 的 middleware:
const logger = ({ getState }) => (next) => (action) => {
console.log(getState());
return next(action);
};
然后在 applyMiddleware
中进行注入:
const AppStore = createStore(appReducer, initialState, applyMiddleware(logger));
这样,每当应用 dispatch action 的时候,都会输出当前的 state。
关于 middleware 的更多阅读资料:
redux-promise 是一个相当简洁的小库,它帮助你完成异步请求以后自动分发到 reducer,并且在失败(reject)的时候停止继续分发。
// 注入 middleware
import promiseMiddleware from 'redux-promise';
const AppStore = createStore(appReducer, initialState, applyMiddleware(promiseMiddleware));
// actions.js
// 在 action 中使用异步
import API from 'some api func';
// API.getThing 返回的应该是个 promise,并且需要直接将结果代入 reducer
export const getThing = createAction('GET_THING', API.getThing);
// 相当于:
const getThing = createAction('GET_THING');
const fetchThing = () => (dispatch, getState) => {
API.getThing.then(result => dispatch(getThing(result)));
};
由此可见,在使用了 redux-promise 之后,在通过 createAction
创建 action 时可以直接代入 promise 。也就是说,redux-promise 中间件在其内部帮我们完成了 promise.then
的调用,并将最终结果传递给了 action.payload
。
redux-promise 源码:
import { isFSA } from 'flux-standard-action';
function isPromise(val) {
return val && typeof val.then === 'function';
}
export default function promiseMiddleware({ dispatch }) {
return next => action => {
// 对于非 FSA 的 action 而言,如果 action 是个 Promise,则调用 then 方法,并把 dispatch 作为参数代入
if (!isFSA(action)) {
return isPromise(action)
? action.then(dispatch)
: next(action);
}
// 否则,这个 action 是一个标准 action,返回值类似:
// {
// type: '',
// payload: '',
// error: bool
// }
return isPromise(action.payload)
? action.payload.then(
result => dispatch({ ...action, payload: result }),
error => {
dispatch({ ...action, payload: error, error: true });
return Promise.reject(error);
}
)
: next(action);
};
}
顺便看一眼 flux-standard-action
:
import isPlainObject from 'lodash.isplainobject';
import isString from 'lodash.isstring';
import isSymbol from 'lodash.issymbol';
export function isFSA(action) {
return (
// action 必须是一个 plan object
isPlainObject(action) &&
// type 是 String 或者 Symbol
(isString(action.type) || isSymbol(action.type)) &&
// action object 的每一个 key 都是合法的,只存在于 ['type', 'payload', 'error', 'meta'] 中
Object.keys(action).every(isValidKey)
);
}
export function isError(action) {
return action.error === true;
}
function isValidKey(key) {
return [
'type',
'payload',
'error',
'meta',
].indexOf(key) > -1;
}