UNPKG

@nebula-note/redux-hooks

Version:

Simplify Redux usage: no actions, no reducers, only hooks.

190 lines (189 loc) 5.39 kB
import _ from "lodash"; import { combineReducers, configureStore as configureStore$1, createSlice } from "@reduxjs/toolkit"; import { useMemo, useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; const takerList = []; let recordId = 0; const removeCallback = (referenceId) => { _.remove(takerList, (taker) => taker.referenceId === referenceId); }; const takeMiddleware = () => (next) => (action) => { const result = next(action); takerList.forEach((taker) => { if ((taker == null ? void 0 : taker.actionType) === action.type) { taker.callback(); } }); return result; }; const addTaker = (actionType, callback) => { const existObj = takerList.find((taker) => taker.referenceId === callback.name); let referenceId = existObj == null ? void 0 : existObj.referenceId; if (!existObj) { recordId++; const takeObj = { referenceId: callback.name ? callback.name : recordId.toString(), actionType, callback }; takerList.push(takeObj); referenceId = takeObj.referenceId; } return () => removeCallback(referenceId); }; function createReducerManager(initialReducers = {}) { const reducers = { ...initialReducers }; let combinedReducer = combineReducers(reducers); let keysToRemove = []; return { getReducerMap: () => reducers, reduce: (state, action) => { if (keysToRemove.length > 0) { state = { ...state }; keysToRemove.forEach((key) => { state == null ? true : delete state[key]; }); keysToRemove = []; } return combinedReducer(state ?? {}, action); }, add: (key, reducer) => { if (!key || reducers[key]) return; reducers[key] = reducer; combinedReducer = combineReducers(reducers); }, remove: (key) => { if (!key || !reducers[key]) return; delete reducers[key]; keysToRemove.push(key); combinedReducer = combineReducers(reducers); } }; } let store; const getStore = () => store; const configureStore = (options = {}) => { if (store) { return store; } const reducerHolder = { __holder: (state = "1.0.0") => state }; const reducerManager = createReducerManager((options == null ? void 0 : options.reducer) ?? reducerHolder); store = configureStore$1({ ...options, reducer: reducerManager.reduce, middleware: (getDefaultMiddleware) => { if (options.middleware) { const middlewares = options.middleware(getDefaultMiddleware); return middlewares.concat(takeMiddleware); } return getDefaultMiddleware().concat(takeMiddleware); } }); reducerManager.remove("__holder"); store.reducerManager = reducerManager; store.addTaker = addTaker; return store; }; const createSliceInstance = (sliceName, initialState) => createSlice({ name: sliceName, initialState, reducers: { setState: (state, action) => { return action.payload; }, updateState: (state, action) => { return _.mergeWith({}, state, action.payload, (objValue, srcValue) => { if (Array.isArray(objValue)) { return srcValue; } }); } } }); const useRedux = (stateName, initialState) => { const store2 = getStore(); const dispatch = useDispatch(); const reducerInstance = useMemo( () => createSliceInstance(stateName, initialState), [stateName, initialState] ); if (!store2) { throw new Error("store is not initialized"); } useEffect(() => { store2.reducerManager.add(stateName, reducerInstance.reducer); }, []); const state = useSelector((storeState) => { if (Object.prototype.hasOwnProperty.call(storeState, stateName)) { return storeState[stateName]; } return initialState; }); const take = (actionType) => { let resolveHandle; const promiseHandle = new Promise((resolve) => { resolveHandle = resolve; }); const removeTaker = store2.addTaker(`${stateName}/${actionType}`, () => { resolveHandle(removeTaker); }); return promiseHandle; }; const takeOnce = (actionType) => { let resolveHandle; const promiseHandle = new Promise((resolve) => { resolveHandle = resolve; }); const removeTaker = store2.addTaker(`${stateName}/${actionType}`, () => { resolveHandle(); removeTaker(); }); return promiseHandle; }; const getStateSync = () => { return store2.getState()[stateName]; }; const setState = (payload) => { if (typeof payload === "function") { const ownState = getStateSync(); const newState = payload(ownState); dispatch(reducerInstance.actions.setState(newState)); return; } dispatch(reducerInstance.actions.setState(payload)); }; const setStateSync = (payload) => { (async () => { const takeHandle = takeOnce("setState"); setState(payload); await takeHandle; })(); }; const updateState = (payload) => { dispatch(reducerInstance.actions.updateState(payload)); }; const updateStateSync = (payload) => { (async () => { const takeHandle = takeOnce("updateState"); updateState(payload); await takeHandle; })(); }; return { state, getStateSync, setState, setStateSync, updateState, updateStateSync, take, takeOnce }; }; export { configureStore, getStore, useRedux };