什么是middleware
action - reducer中加入逻辑,提供逻辑插入点;也可以称之为store的enhencer;store enhencer是高阶函数(接受function作为参数,并且return function);
注意:部分代码为伪代码
如何开发一个middleware
首先,我们要知道,我们是如何加入一个middleware的;初始化代码: middleware的格式:
const middleware = store => next => action => {
// your code
next(action);
}
核心-compose
目的:为了方便地书写深度嵌套的函数,而不用手动一次一次地调用,具体实现就是使用了reduce compose源码中可以看到,就是使用reduce,将函数串联起来,即,入参a,b,c,返回:a(b(c(...args)));
export default function compose(...funcs: Function[]) {
if (funcs.length === 0) {
// infer the argument type so it is usable in inference down the line
return <T>(arg: T) => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce(
(a, b) =>
(...args: any) =>
a(b(...args))
)
}
applymiddleware源码:
export default function applyMiddleware(
...middlewares: Middleware[]
): StoreEnhancer<any> {
return (createStore: StoreEnhancerStoreCreator) =>
<S, A extends AnyAction>(
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S>
) => {
// 创建store
const store = createStore(reducer, preloadedState)
let dispatch: Dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}
const middlewareAPI: MiddlewareAPI = {
getState: store.getState,
dispatch: (action, ...args) => dispatch(action, ...args)
}
// 函数柯里化,嵌套层次太深,预先传入参数
/// 所有middleware传入middlewareapi-第一次,预先传入getstate和dispatch;
// 此时的chain,只接受next和action参数
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 在compose中,进行函数的组合,之后在这里,compose之后,得到middlware1(middlware2(...args))
// 预先传入next参数,next就是store.dispatch, middlware1(middlware2(...args))(store.dispatch),为最后一个middleware传入dispatch参数
dispatch = compose<typeof dispatch>(...chain)(store.dispatch)
// dispatch被覆盖为包装过的dispatch,新的dispatch其实是middlware1(middlware2(...args))(store.dispatch)
// 接受action,最后调用dispatch的时候,就是在执行dispatch(action),
// middlware1(middlware2(...args))(store.dispatch)(action)
// middleware3接受的next是dispatch,其余的next是上一个函数,1的next是2,2的next是3,3的next是dispatch
// 因此,在上一个middlware中,需要手动执行next(action),才能顺利执行下一个middleware;
return {
...store,
dispatch
}
}
}
上面注释如果不清楚的,可以看这里:
const dispatch = (action) => {
console.log('dispatch', action);
}
function a(next) {
return (action) => {
console.log(action, 'a', next);
next(action);
};
}
function b(next) {
return (action) => {
console.log(action, 'b', next);
next(action)
};
}
function c(next) {
return (action) => {
console.log(action, 'c', next);
next(action);
};
}
// 执行应该是c-b-a;但是c返回的函数是b的入参,b返回的是a的入参数,最后返回的是a函数,('test')action从a函数开始;
const fn = a(b(c(dispatch)));
console.log(fn);
// (action) => {
// console.log(action, 'a', next);
// next(action);
// }
fn('test');
// 执行结果
// test a (action) => {
// console.log(action, 'b', next);
// next(action)
// }
// test b (action) => {
// console.log(action, 'c', next);
// next(action);
// }
// test c (action) => {
// console.log('dispatch', action);
// }
// dispatch test
简单的示例,一样的原理,a的next是b,b的next是c,c的next是最初调用传入的值dispatch
如何理解redux-thunk
what
什么是thunk呢? 简单理解,就是函数返回一个函数,return的function就是thunk,它推迟了内部返回函数逻辑的执行,如下所示就是thunk thunk可以是匿名函数,也可以是具名函数
function wrapFn() {
// 这里就是一个thunk,被推迟了执行
return () => {
console.log('i am a thunk function')
}
}
如上可见,我们如果要调用这个thunk,我们需要调用两次wrapper function,即wrapFn()();但是redux在上面的分析中,做了的处理,可以让我们只调用一次就正常调用;
why-为什么异步我们需要thunk?
redux重要的概念有action,reducer,store,actionCreator,dispatch;
如果需要触发一个state的改动,那需要dispatch一个action,action会被传递到reducer当中,reducer是纯函数,action是一个普通的plain object; redux-thunk允许我们在actioncreator中使用function
同步操作:直接触发普通的action即可
function add() {
return {
type: 'ADD',
payload: 1,
}
}
异步操作:普通的异步调用函数如下
function serverAdd() {
return fetch.get('/xxxxx').then((res) => {
// your code
});
}
reducer是纯函数,不能在reducer中做异步,应该在action做异步,普通的纯对象,无法满足异步(通常使用promise)的要求,我们可以返回一个函数,在函数中做异步操作
此时会发现,actioncreator返回的是function,无法正确识别,触发reducer;
如果不使用thunk,我们会如何操作?如何实现?
不使用thunk,我们可以手动将dispatch传入到函数,在异步完成之后,再调用dispatch
function serverAdd(dispatch, userId) {
return fetch.get(`/${userId}`).then((res) => {
// your code
dispatch({type: 'ADD'});
});
}
此时,每个方法都需要手动传入dispatch,而且需要明确地区分方法是同步/异步方法,会使代码变得复杂,不易于扩展; thunk让我们哪些变得简单了 使用thunk,我们无需手动去传递dispatch,调用thunk就像调用普通的action一样自然;而且也可以识别到function
function serverAdd(userId) {
return dispatch => fetch.get(`/${userId}`).then((res) => {
// your code
dispatch({type: 'ADD'});
});
}
// 调用
dispatch(serverAdd(1));
how-原理
thunk的源码解析 上面可以看到,thunk是返回了一个函数,只要加入一个中间件,能够识别到函数类的action,并对这类action直接调用即可;
function createThunkMiddleware<
State = any,
BasicAction extends Action = AnyAction,
ExtraThunkArg = undefined
>(extraArgument?: ExtraThunkArg) {
// Standard Redux middleware definition pattern:
// See: https://redux.js.org/tutorials/fundamentals/part-4-store#writing-custom-middleware
const middleware: ThunkMiddleware<State, BasicAction, ExtraThunkArg> =
({ dispatch, getState }) =>
next =>
action => {
// The thunk middleware looks for any functions that were passed to `store.dispatch`.
// If this "action" is really a function, call it and return the result.
if (typeof action === 'function') {
// Inject the store's `dispatch` and `getState` methods, as well as any "extra arg"
return action(dispatch, getState, extraArgument)
}
// Otherwise, pass the action down the middleware chain as usual
return next(action)
}
return middleware
}
thunk中间件,每一个action都会经过thunk,thunk进行判断,如果是函数类型的,直接执行,否则,next其实就是dispatch,dispatch普通的action
竟品
redux-saga-不同点
参考:
daveceddia.com/what-is-a-t…
github.com/reduxjs/red…
stackoverflow.com/questions/3…
medium.com/@istvanistv…