React@16.x(60)Redux@4.x(9)- 实现 applyMiddleware

接上篇文章:Redux中间件介绍

1,applyMiddleware 原理

Redux 应用中间件方式:

const store = createStore(reducer, applyMiddleware(logger1, logger2));

实际会转为下面的方式执行:

const store = applyMiddleware(logger1, logger2)(createStore)(reducer)
  • applyMiddleware 会确定用到的中间件;它会返回一个用来创建仓库的函数A,参数createStore
  • 函数 A,会返回创建仓库的函数B,和 createStore 函数差不多。
  • 函数B,会对中间件函数做处理,并修改原始 dispatch

大致相当于:

export const applyMiddleware = (...middlewares) => {
	// 用来创建的仓库的函数
    return (createStore) => {
    	// 创建仓库的函数
        return (reducer, defaultState) => {
            const store = createStore(reducer, defaultState)
            const dispatch = '经过 middlewares 处理的 store.dispatch'
            return {
                ...store,
                dispatch
            }
        }
    }
}

2,实现

2.1,applyMiddleware

上篇文章介绍了 中间件函数 的写法和多个中间件函数的执行顺序。

基于此,实现这个 dispatch 流转的逻辑,并得到最终的 dispatch 即可完成 applyMiddleware

2.1.1,compose 方法

关键要实现

const resultDispatch = logger1(logger2(logger3(store.dispatch)))

实现:

/**
 * @param  {...any} funcs
 * @returns {function}
 */
export const compose = (...funcs) => {
    if (funcs.length === 0) {
        return (args) => args;
    } else if (funcs.length === 1) {
        return funcs[0];
    }
    return (...args) => {
        let lastReturn = null;
        for (let i = funcs.length - 1; i >= 0; i--) {
            const func = funcs[i];
            if (i === funcs.length - 1) {
                lastReturn = func(...args);
            } else {
                lastReturn = func(lastReturn);
            }
        }
        return lastReturn;
    };
};
// 测试代码
const add = (n) => {
    return n + n;
};

const mult = (n) => {
    return n * n;
};

const b = compose(add, mult);
console.log(b(3)); // 先乘后加,18

可使用 Array.reduce 简化:

export const compose = (...funcs) =>
    funcs.reduce(
        (prev, next) =>
            (...args) =>
                prev(next(...args))
    );

2.1.2,applyMiddleware

import { compose } from "./compose";
export const applyMiddleware = (...middlewares) => {
    return (createStore) => {
        return (reducer, defaultState) => {
            const store = createStore(reducer, defaultState);
            let dispatch = () => {
                throw new Error("目前还不能使用 dispatch");
            };

            // 传递给中间件函数的 store 只有这2个属性。
            const simpleStore = {
                getState: store.getState,
                dispatch: (...args) => dispatch(...args), // 每个中间件函数的 dispatch 都是上一个中间件组装后的
            };
            // 获取用于创建 dispatch 的函数
            const dispatchProducts = middlewares.map((m) => m(simpleStore));
            // 重新组装后的 dispatch
            dispatch = compose(...dispatchProducts)(store.dispatch);
            return {
                ...store,
                dispatch,
            };
        };
    };
};

2.2,修改 createStore

之前实现的 createStore,没有对可能的第3个函数做处理。这里补充下:

  • 如果第2个参数是 applyMiddleware,那说明没有 defaultState

这里就简单判断写第2个参数是不是函数,实际源码中 defaultState 也可以通过一个函数创建。

export const createStore = (reducer, defaultState, enhancer) => {
    // enhancer 表示 applymiddleware 返回的函数。
    if (typeof defaultState === 'function') {
        enhancer = defaultState
        defaultState = undefined
    }
    if (typeof enhancer === 'function') {
        enhancer(createStore)(reducer, defaultState)
    }
    // 其他剩余代码没有做变动。
    // ...
}

完整代码

export const createStore = (reducer, defaultState, enhancer) => {
    // enhancer 表示 applymiddleware 返回的函数。
    if (typeof defaultState === "function") {
        enhancer = defaultState;
        defaultState = undefined;
    }
    if (typeof enhancer === "function") {
        enhancer(createStore)(reducer, defaultState);
    }
    
    let currentReducer = reducer;
    let currentState = defaultState;
    let listeners = [];

    const dispatch = (action) => {
        if (typeof action !== "object" || Object.getPrototypeOf(action) !== Object.prototype) {
            throw new Error("action 必须是一个 plain Object");
        }
        if (action.type === undefined) {
            throw new Error("action 必须有 type 属性");
        }
        currentState = currentReducer(currentState, action);
        // 每次更新时,遍历监听器。
        for (const listener of listeners) {
            listener();
        }
    };

    const getState = () => {
        return currentState;
    };

    const subscribe = (listener) => {
        listeners.push(listener);
        let isRemove = false;
        // 取消监听
        return () => {
            if (isRemove) {
                return;
            } else {
                isRemove = true;
                listeners = listeners.filter((f) => f !== listener);
            }
        };
    };

    // createStore 创建时会调用一次。
    dispatch({
        type: `@@redux/INIT${getRandomString}`,
    });

    return {
        dispatch,
        getState,
        subscribe,
    };
};

function getRandomString() {
    return Math.random().toString(36).substring(2, 8).split("").join(".");
}

以上。

相关推荐

  1. React@16.x(54)Redux@4.x(3)- reducer

    2024-07-17 10:00:04       22 阅读
  2. React@16.x(56)Redux@4.x(5)- 实现 createStore

    2024-07-17 10:00:04       16 阅读
  3. React@16.x(58)Redux@4.x(7)- 实现 combineReducers

    2024-07-17 10:00:04       19 阅读
  4. React@16.x(57)Redux@4.x(6)- 实现 bindActionCreators

    2024-07-17 10:00:04       25 阅读
  5. React@16.x(55)Redux@4.x4)- store

    2024-07-17 10:00:04       21 阅读
  6. React@16.x(53)Redux@4.x(2)- action

    2024-07-17 10:00:04       21 阅读
  7. React@16.x(52)Redux@4.x(1)- 核心概念

    2024-07-17 10:00:04       19 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-17 10:00:04       53 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-17 10:00:04       56 阅读
  3. 在Django里面运行非项目文件

    2024-07-17 10:00:04       46 阅读
  4. Python语言-面向对象

    2024-07-17 10:00:04       57 阅读

热门阅读

  1. 开发版的驱动开发

    2024-07-17 10:00:04       21 阅读
  2. 如何将下载下来excel只读模式改成可编辑

    2024-07-17 10:00:04       22 阅读
  3. 提取文本中所有图片的链接地址

    2024-07-17 10:00:04       20 阅读
  4. 【渗透入门】XSS

    2024-07-17 10:00:04       21 阅读
  5. Hadoop:HDFS-磁盘读写速度检测(实用)

    2024-07-17 10:00:04       26 阅读
  6. R-CNN、Fast R-CNN和Faster R-CNN:目标检测的进化之路

    2024-07-17 10:00:04       21 阅读
  7. 三生随记——空调的诅咒

    2024-07-17 10:00:04       16 阅读
  8. 数据结构常用算法理论

    2024-07-17 10:00:04       22 阅读