内容简介:多数本来写这篇文章只是想写写
多数 redux
初学者都会使用 redux-thunk
这个中间件来处理异步请求(比如我)
本来写这篇文章只是想写写 redux-thunk
,然后发现还不够,就顺便把 middleware
给过了一遍。
为什么叫 thunk
?
thunk
是一种包裹一些稍后执行的表达式的函数。
redux-thunk
源码
所有的代码就只有15行,我说的是真的。。 redux-thunk
function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk; 复制代码
代码很精简,但是功能强大,所以非常有必要去了解一下。
redux-middleware是个啥
上图描述了一个 redux
中简单的同步数据流动的场景,点击 button
后, dispatch
一个 action
,reducer 收到 action 后,更新 state
后告诉 UI
,帮我重新渲染一下。
redux-middleware
就是让我们在 dispatch
action
之后,在 action
到达 reducer
之前,再做一点微小的工作,比如打印一下日志什么的。试想一下,如果不用 middleware
要怎么做,最 navie
的方法就是每次在调用 store.dispatch(action)
的时候,都 console.log
一下 action
和 next State
。
store.dispatch(addTodo('Use Redux')); 复制代码
- 最
naive
的方法,唉,每次都写上吧
const action = addTodo('Use Redux'); console.log('dispatching', action); store.dispatch(action); console.log('next state', store.getState()); 复制代码
- 既然每次都差不多,那封装一下吧
function dispatchAndLog(store, action) { console.log('dispatching', action); store.dispatch(action); console.log('next state', store.getState()); } 复制代码
- 现在问题来了,每次
dispatch
的时候都要import
这个函数进来,有点麻烦是不是,那怎么办呢?
既然 dispatch
是逃不走的,那就在这里动下手脚, redux
的 store
就是一个有几种方法的对象,那我们就简单修改一下 dispatch
方法。
const next = store.dispatch; store.dispatch = function dispatchAndLog(action) { console.log('dispatching', action); next(action); // 之前是 `dispatch(action)` console.log('next state', store.getState()); } 复制代码
这样一来我们无论在哪里 dispatch
一个 action
,都能实现想要的功能了,这就是中间件的雏形。
- 现在问题又来了,大佬要让你加一个功能咋办?比如要异常处理一下
接下来就是怎么加入多个中间件了。
function patchStoreToAddLogging(store) { const next = store.dispatch store.dispatch = function dispatchAndLog(action) { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result } } function patchStoreToAddCrashReporting(store) { const next = store.dispatch store.dispatch = function dispatchAndReportErrors(action) { try { return next(action) } catch (err) { console.error('Caught an exception!', err) Raven.captureException(err, { extra: { action, state: store.getState() } }) throw err } } } 复制代码
patchStoreToAddLogging
和 patchStoreToAddCrashReporting
对 dispatch
进行了重写,依次调用这个两个函数之后,就能实现打印日志和异常处理的功能。
patchStoreToAddLogging(store) patchStoreToAddCrashReporting(store) 复制代码
- 之前我们写了一个函数来代替了
store.dispatch
。如果直接返回一个新的dispatch
函数呢?
function logger(store) { const next = store.dispatch // 之前: // store.dispatch = function dispatchAndLog(action) { return function dispatchAndLog(action) { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result } } 复制代码
这样写的话我们就需要让 store.dispatch
等于这个新返回的函数,再另外写一个函数,把上面两个 middleware
连接起来。
function applyMiddlewareByMonkeypatching(store, middlewares) { middlewares = middlewares.slice() middlewares.reverse() // Transform dispatch function with each middleware. middlewares.forEach(middleware => (store.dispatch = middleware(store))) } 复制代码
middleware(store)
会返回一个新的函数,赋值给 store.dispatch
,下一个 middleware
就能拿到一个的结果。
接下来就可以这样使用了,是不是优雅了一些。
applyMiddlewareByMonkeypatching(store, [logger, crashReporter]) 复制代码
我们为什么还要重写 dispatch
呢?当然啦,因为这样每个中间件都可以访问或者调用之前封装过的 store.dispatch
,不然下一个 middleware
就拿不到最新的 dispatch
了。
function logger(store) { // Must point to the function returned by the previous middleware: const next = store.dispatch return function dispatchAndLog(action) { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result } } 复制代码
连接 middleware
是很有必要的。
但是还有别的办法,通过柯里化的形式, middleware
把 dispatch
作为一个叫 next
的参数传入,而不是直接从 store
里拿。
function logger(store) { return function wrapDispatchToAddLogging(next) { return function dispatchAndLog(action) { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result } } } 复制代码
柯里化就是把接受多个参数的函数编程接受一个单一参数(注意是单一参数)的函数,并返回接受余下的参数且返回一个新的函数。
举个例子:
const sum = (a, b, c) => a + b + c; // Curring const sum = a => b => c => a + b + c; 复制代码
用 ES6
的箭头函数,看起来更加舒服。
const logger = store => next => action => { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result } const crashReporter = store => next => action => { try { return next(action) } catch (err) { console.error('Caught an exception!', err) Raven.captureException(err, { extra: { action, state: store.getState() } }) throw err } } 复制代码
接下来我们就可以写一个 applyMiddleware
了。
// 注意:这是简单的实现 function applyMiddleware(store, middlewares) { middlewares = middlewares.slice() middlewares.reverse() let dispatch = store.dispatch middlewares.forEach(middleware => (dispatch = middleware(store)(dispatch))) return Object.assign({}, store, { dispatch }) } 复制代码
上面的方法,不用立刻对 store.dispatch
赋值,而是赋值给一个变量 dispatch
,通过 dispatch = middleware(store)(dispatch)
来连接。
现在来看下 redux
中 applyMiddleware
是怎么实现的?
/** * Composes single-argument functions from right to left. The rightmost * function can take multiple arguments as it provides the signature for * the resulting composite function. * * @param {...Function} funcs The functions to compose. * @returns {Function} A function obtained by composing the argument functions * from right to left. For example, compose(f, g, h) is identical to doing * (...args) => f(g(h(...args))). */ // 就是把上一个函数的返回结果作为下一个函数的参数传入, compose(f, g, h)和(...args) => f(g(h(...args)))等效 export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) } 复制代码
compose
最后返回的也是一个函数,接收一个参数 args
。
export default function applyMiddleware(...middlewares) { return createStore => (...args) => { const store = createStore(...args) let dispatch = () => { throw new Error( `Dispatching while constructing your middleware is not allowed. ` + `Other middleware would not be applied to this dispatch.` ) } const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } // 确保每个`middleware`都能访问到`getState`和`dispatch` const chain = middlewares.map(middleware => middleware(middlewareAPI)) // wrapDispatchToAddLogging(store) dispatch = compose(...chain)(store.dispatch) // wrapCrashReport(wrapDispatchToAddLogging(store.dispatch)) return { ...store, dispatch } } } 复制代码借用一下大佬的图,
google搜索redux-middleware第一张
到这里我们来看一下 applyMiddleware
是怎样在 createStore
中实现的。
export default function createStore(reducer, preloadedState, enhancer){ ... } 复制代码
createStore
接受三个参数: reducer
, initialState
, enhancer
。 enhancer
就是传入的 applyMiddleware
函数。
//在enhancer有效的情况下,createStore会返回enhancer(createStore)(reducer, preloadedState)。 return enhancer(createStore)(reducer, preloadedState) 复制代码
我们来看下刚刚的 applyMiddleware
,是不是一下子明白了呢。
return createStore => (...args) => { // .... } 复制代码
到这里应该就很容易理解 redux-thunk
的实现了,他做的事情就是判断 action
类型是否是函数,如果是就执行 action
,否则就继续传递 action
到下个 middleware
。
参考文档:
以上所述就是小编给大家介绍的《深入浅出redux-middleware》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 【1】JavaScript 基础深入——数据类型深入理解与总结
- 深入理解 Java 函数式编程,第 5 部分: 深入解析 Monad
- 深入理解 HTTPS
- 深入理解 HTTPS
- 深入浅出Disruptor
- 深入了解 JSONP
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Producing Masculinity
Michele White / Routledge / 2019-3 / $39.95
Thoughtful, witty, and illuminating, in this book Michele White explores the ways normative masculinity is associated with computers and the Internet and is a commonly enacted online gender practice. ......一起来看看 《Producing Masculinity》 这本书的介绍吧!
HTML 压缩/解压工具
在线压缩/解压 HTML 代码
HTML 编码/解码
HTML 编码/解码