redux applyMiddleware

卢英叡
2023-12-01

redux中间件介绍

  1. 用途:主要用于处理异步数据流;redux中间件的实质是对storedispatch进行重新和包装,修改store.dispatch的默认行为;redux中间件是对redux功能的一种扩展,也是扩展dispatch的唯一标准方式;
  2. 特点:可以链式调用;
  3. 既然其本质是对store.dispatch的重新,那么如果不计代码整洁性及冗杂性,任意的middleware均可以手写实现;

进一步分析中间件

  1. 假如我们需要在store.dispatch前后需要查看对应的action以及state,我们需要这么实现:

    let action = addTodo('learning redux middlewares');
    console.log('dispatched action: ', action);
    store.dispacth(action);
    console.log('nextState: ', store.getState());
    
  2. 如果上述功能是为了我们方便查看日志,显然将其封装为一个函数更方便;

    function dispatchAndLog(store, action) {
    	console.log('dispatched action: ', action);
    	store.dispacth(action);
    	console.log('nextState: ', store.getState());
    }
    
  3. 这样相对方便了,但是每次使用我们需要引入这个函数;如果我们还想偷懒,可以通过调用原生的store.dispatch就可以实现这些功能岂不是美滋滋~
    于是,我们开始重新`store.dispatch

    const next = store.dispatch;
    store.dispatch = function (action) {
    	console.log('dispatched action: ', action);
    	let result = next(action);
    	console.log('next state: ', store.getState());
    	return result;
    }
    
  4. 参考redux的源码我们知道,store.dispatch函数接受一个action参数,返回同一个dispacthed action;因此,我们需要对该段代码继续优化;

    function dispatchAndLogWrapper (store) {
    	const next = store.dispatch;
    	
    	return function dispatchAndLog(action) {
    		console.log('dispatched action: ', action);
    		let result = next(action);
    		console.log('nextState: ', store.getState());
    		return result;
    	}
    }
    

    这样,我们中间件返回一个被middleware重写后的store.dispatch函数;这样奠定了链式调用的基础;
    但是对于链式调用,后一个中间件对store.dispatch的修改是基于前一个中间件修改的基础上进行的;而上述实现中我们的next总是为固定值,即原生的store.dispatchnext的期望值应当是上一个中间件重写后的store.dispatch;因此,中间件的实现中next通常作为参数传入;next的初始值取原生的store.dispatch

  5. 中间件函数的形式描述为:({ getState, dispatch }) => next => action
    据此,改写我们的模拟中间件实现:

    const dispatchAndLogWrapper = store => next => action => {
        console.log('dispatch: ', action);
        let result = next(action);
        console.log('next state: ', store.getState())
        return result;
    }
    
  6. 模拟实现applyMiddleware
    applyMiddleware可以实现对中间件的链式调用,

    function applyMiddleware(store, middlewares) {
    	middlewares = middlewares.slice()
    	middlewares.reverse(); 	// applyMiddleware其实是store Enhancer扩展机制的一个范例,因此也遵循compose从右至左的组合方式;
    	let dispatch = store.dipatch;	// 获取初始的dispatch
    	// 这里next的初始值为store.dispatch,每次调用一个middleware都是在上一个middleware返回的dispatch的基础进行修改;
    	middlewares.map(middleware => {
    		dispatch =	middleware(store)(dispatch);	//	中间件函数的形式描述为:`({ getState, dispatch }) => next => action`
    	})
    	reurn {
    		...store,
    		dispatch
    	};		// 将dispatch的修改应用到store并返回修改后的store
    	
    }
    

redux-thunk源码解读

// source code
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;

使用方法:

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

// Note: this API requires redux@>=3.1.0
const store = createStore(rootReducer, applyMiddleware(thunk));

分析:

首先可以看到,其实redux-thunk对我们模拟的中间件外部多了一层封装,用于接受外部传入的其他参数;导出的是函数createThunkMiddleware的执行结果,即返回结果与我们的模拟结构类似;

其次,next的来源不同;redux-thunk实现中,next是作为参数传入的而不是直接赋值为store.dispatch

另外,内部实现对action的类型进行了判断并分别进行了处理:是对象还是Action Creator函数;


applyMiddleware源码分析

// 函数形式描述为:(...middlewares)=>createStore=>(...args)
function applyMiddleware (...middlewares) {
    return createStore => (...args) => {	// 返回应用了中间件的一个store Enhancer
        const store = createStore(...args);
        const dispatch = store.dispatch;

        const middlewareAPI = {
            getState: store.getState,
            dispatch: (...args) => dispatch(...args)
        }
        // 这里其实把middlewares.map(middleware => {dispatch =	middleware({getState, dispatch})(dispatch)})分为了两步,chain是一个数组,数组的成员为每个调用了middlewareAPI的中间件
        const chain= middlewares.map(middleware => middleware(middlewareAPI))	
        dispatch = compose(...chain)(store.dispatch);	

        return {				
            ...store,
            dispatch
        }
    }
}


参考文献

  1. segmentfault redux源码解析系列
  2. redux store源码
  3. redux applyMiddleware源码
  4. redux-thunk github
  5. understanding middleware
 类似资料:

相关阅读

相关文章

相关问答