React 实现简单的 redux [dispatch]

202 阅读1分钟

React 实现简单的 redux [dispatch]

创建Store.js 文件

import React, {
    createContext,
    useCallback,
    useReducer,
    useContext
} from "react";

// dispatch 命名空间Map
const Dispatch = new Map();

function useStore({ reducers, namespace, effects, state }) {
    const run = () => {
        const dispatchFn = useCallback(async ({ type, payload }) => {
            const fn = effects[type];
            if (fn) {
                try {
                    put({
                        type: "setLoading",
                        payload: {
                            [type]: true
                        }
                    });
                    await fn(payload, put);

                } finally {
                    put({
                        type: "setLoading",
                        payload: {
                            [type]: false
                        }
                    });
                }
            } else {
                put({
                    type,
                    payload
                });
            }
        }, []);

        const dispatch = useCallback(
            ({ type, payload }) => {
                if (type.split("/").length) {
                    const [ns, part] = type.split("/");
                    const nsDis = Dispatch.get(ns);
                    if (nsDis) {
                        const newType = { type: part, ...payload };
                        nsDis.dispatch(newType);
                    } else {
                        dispatchFn({ type, payload });
                    }
                }
            },
            [dispatchFn]
        );

        Dispatch.set(namespace, dispatch);

        const reducerFn = useCallback((state, action) => {
            const { type, payload } = action;

            if (type === "setLoading") {
                return {
                    ...state,
                    loading: { ...(state.loading || {}), ...payload }
                };
            } else {
                const data = reducers[type](state, payload);
                return data;
            }

        }, []);
        const [store, put] = useReducer(reducerFn, state);
        return [store, dispatch];
    };
    return run;
}

const createStore = () => {
    const context = createContext();
    const useConnect = mapfn => {
        const [val, dispatch] = useContext(context);
        return [mapfn ? mapfn(val) : val, dispatch];
    };
    return [context, useConnect];
};

export { createStore, useStore };

定义一个model.js

import { useStore, createStore } from "./store";

const Model = {
    namespace"count",
    state: {
        num0
    },
    effects: {
        async fetchNum(state, put) {
            await new Promise(resolve => {
                setTimeout(resolve, 1000);
            });
            // 模拟请求
            const data = await fetch("/a.json").then(res => res.json());
            put({
                type"add",
                payload: data.num

            });
        }
    },
    reducers: {
        add(state, payload) {
            return {
                num: state.num + payload
            };
        },
        decrement(state, payload) {
            return {
                ...state,
                num: state.num - payload
            };
        }
    }
};


const CountStore = useStore(Model);
const Ctx = createStore();


export { CountStoreCtx };

Demo

import { CountStoreCtx } from "./model";
import {
    memo,
    useState,
} from "react";

export default () => {
    const [Context] = Ctx;
    const store = CountStore();
    console.log("context");
    return (
        <Context.Provider value={store}>
            <Counnt></Counnt>
        </Context.Provider>
    );
};

const Counnt = memo(() => {
    const [, useConnect] = Ctx;
    const [state, dispatch] = useConnect();
    console.log(state);

    return (
        <>
            <Button className='Groupe'></Button>
            <Button onClick={() => dispatch({ type: "add", payload: 10 })}>
                add
            </Button>
            <Button onClick={() => dispatch({ type: "decrement", payload: 5 })}>
                decrement
            </Button>
            <Button
                loading={(state.loading && state.loading["fetchNum"]) || false}
                onClick={() => dispatch({ type: "fetchNum" })}>
                fetchNum
            </Button>
            count is {state.num}
        </>
    );
});

演示

tutieshi_640x362_16s.gif