weavedev/store

View on GitHub
lib/reducers.ts

Summary

Maintainability
A
2 hrs
Test Coverage
import { combineReducers, Reducer } from 'redux';
import { envGlobal } from './envGlobal';

type StoreReducer = Reducer<StoreState, StoreActions>;
type StoreKey = number | string;

export function watchWindowStoreReducers(): StoreReducer {
    // Place to keep removed reducer keys untill we are able to clean up when redux recalculates its store state
    let deletedReducers: StoreKey[] = [];
    // Constructed reducer
    let storeReducer: StoreReducer = combineReducers(envGlobal.storeReducers);
    // Wrap window reducers object and copy existing reducers
    envGlobal.storeReducers = new Proxy(envGlobal.storeReducers, {
        deleteProperty(target: StoreReducersMap, k: string | symbol): boolean {
            if (k === undefined || !target[<keyof StoreReducersMap>k]) {
                return true;
            }

            delete target[<keyof StoreReducersMap>k]; // tslint:disable-line:no-dynamic-delete

            // Clearing the state for the deleted reducer.
            deletedReducers.push(<keyof StoreReducersMap>k);

            storeReducer = combineReducers(envGlobal.storeReducers);

            envGlobal.store.dispatch({ type: '$$REMOVE_REDUCER', reducer: `${String(k)}` });

            return true;
        },
        set(target: StoreReducersMap, k: string | symbol, value: any): boolean {
            if (k === undefined || k === '') {
                return true;
            }

            // Clearing the state for the new reducer. (Preserving state should be done manually)
            if (target[<keyof StoreReducersMap>k]) {
                deletedReducers.push(<keyof StoreReducersMap>k);
            }

            // @ts-ignore
            target[<keyof StoreReducersMap>k] = value; // tslint:disable-line:no-unsafe-any

            storeReducer = combineReducers(envGlobal.storeReducers);

            envGlobal.store.dispatch({ type: '$$SET_REDUCER', reducer: `${String(k)}` });

            return true;
        },
    });

    // Main reducer
    return (state: StoreState | undefined, action: StoreActions): StoreState => {
        const newState: StoreState = <StoreState>{ ...state }; // tslint:disable-line:no-object-literal-type-assertion
        if (deletedReducers.length > 0) {
            deletedReducers.forEach((k: StoreKey): void => {
                delete newState[k]; // tslint:disable-line:no-dynamic-delete
            });
            deletedReducers = [];
        }

        return storeReducer(newState, action);
    };
}