巧用Redux

發表於2023-12-12

Redux類適用所有React生態專案

import {
    applyMiddleware,
    combineReducers,
    legacy_createStore as createStore,
    Store,
    compose,
} from "redux";
import thunk from "redux-thunk";
import {connect, Provider} from "react-redux";
import {Action as ReduxAction, AnyAction} from "redux";

type StoreState = {
    [key: string]: string | number | boolean | undefined | null | unknown | void
}

type StoreMap<T = any, S = any> = {
    namespace: string;
    state: S | StoreState;
    reducers?: {
        [key: string]: (state: StoreState, params: T) => StoreState
    },
    [key: string]: any;
}

interface StoreStore<T = any> {
    namespace: string;
    state: T | StoreState;
    
    [key: string]: any;
}


type StoreStores<T = any> = {
    [key: string]: StoreStore<T>;
};

type StoreReducers<T = any> = {
    [key: string]: T
}

interface Dispatch<A extends ReduxAction = AnyAction> {
    <T extends A> (action: T): T
}

type Params = { [key: string]: any }

export default class Redux<T = Params> {
    public static _collection: Params = {};
    public dispatch: Dispatch;
    public namespace: string;
    public state: T = {} as T;
    
    constructor () {
    
    }
    
    // 所有狀態的集合
    public get collection () {
        return Redux._collection;
    };
    
    // 初始化的狀態
    public get store () {
        return {
            namespace: this.namespace,
            state: this.state,
        } as {
            namespace: string;
            state: T
        };
    }
    
    // dispatch
    public get action () {
        return (dispatch: Dispatch) => this._renderRedux(dispatch);
    }
    
    // 狀態改變
    public setState = (state: T, cb?: (newState: T) => void): T => {
        this.dispatch({
            type: `${this.namespace}/setState`,
            state
        });
        const newState = {
            ...this.state,
            ...state
        };
        this.state = newState;
        if (cb) {
            cb(newState);
        }
        Redux._collection[this.namespace] = newState;
        return newState;
    };
    
    // 建立多個狀態
    public createStores (
        stores: StoreStores
    ): Store {
        return createStore(
            combineReducers(
                this._createReducers(
                    {
                        ...stores
                    }
                )
            ), compose(applyMiddleware(thunk)
            )
        );
    }
    
    // 根據每個store建立xxx/setState
    private _createState (map: StoreMap) {
        const newMap = {...map};
        newMap.reducers = newMap.reducers ? newMap.reducers : {};
        const newReducers: StoreReducers = {};
        newReducers[`${newMap["namespace"]}/setState`] = (_state: StoreState, {state}: { state: StoreState }) => ({..._state, ...state});
        newMap.reducers = newReducers;
        return (state = newMap.state, action: { type: string }) => {
            if (newMap.reducers && newMap.reducers[action.type]) {
                return newMap.reducers[action.type](state, action);
            } else {
                return state;
            }
        };
    };
    
    private _createReducers (reducers: StoreStores): StoreReducers {
        return Object.entries(reducers).reduce((preItems, item) => {
            const namespace = item[1].namespace;
            Redux._collection[namespace] = item[1].state;
            return {
                ...preItems,
                [namespace]: this._createState(item[1])
            };
        }, {});
    };
    
    private _renderRedux = (dispatch: Dispatch) => {
        this.dispatch = dispatch;
        return this;
    };
}

export {
    Dispatch,
    StoreMap,
    StoreReducers,
    StoreState,
    StoreStores,
    StoreStore,
    Redux,
    connect,
    Provider,
};

繼承Redux

import Redux from "@/common/redux/redux";


interface State {
    desc?: string;
    title?: string;
}
// 繼承Redux
class CommonAction extends Redux<State> {
    constructor () {
        super();
        // 這個狀態的名字(必須要有)
        this.namespace = 'common';
        // 需要管理的單個狀態(必須要有)
        this.state = {
            a: '',
            b: '',
        }
    }
    
    public onChange = async () => {
        // 所有狀態的資料 this.collection['common'] = this.state
        this.collection;
        // 當前common狀態
        this.state;
        // 修改common狀態
        this.setState({ a: 1, b: 2 });
        // 修改其他狀態(這裡是home狀態)
        this.dispatch({type: 'home/setState', {c: 3, d: 4}})
    };

    
}

const common = new CommonAction();

export const commonAction = common.action;
export const commonStore = common.store;

把資料放到全域性(可以放多個Store)

import React from "react";
import Taro from "@tarojs/taro";
import Redux, {Provider} from "@/common/redux/redux";
// 各個狀態
import {commonStore} from "@/common/redux/common";
import {homeStore} from "@/common/redux/home";



const createStoreChildren = (children) => {
    // 狀態各個註冊
    const stores = React.useRef(new Redux().createStores({
        commonStore,
        homeStore
    })).current;
    return <Provider store={stores}>
        {children}
    </Provider>;
};

// createStoreChildren用來包裹最外層
export default createStoreChildren;

單個頁面或者元件引入使用

...
export default connect((e: {}) => {
    // 把當前頁面或者元件匯入用到的狀態值
    return {
        a: e['common'].a,
        b: e['common'].b,
        c: e['home'].c,
        d: e['home'].d,
    };
}, (dispatch) => {
    // 把當前頁面或者元件匯入用到的方法
    const home = homeAction(dispatch);
    const common = commonAction(dispatch);
    return {
        dispatch,
        setHomeState: home.setState,
        homeInit: home.homeInit,
        setCommonState: common.setState,
        onChange: common.onChange,
        
    };
})(Index);

相關文章