Redux
  • GlobalContext Api
  • Redux Easy
  • temp-practice
Powered by GitBook
On this page

Was this helpful?

Redux Easy

createReduxSlice.tsx
import { bindActionCreators, createSlice, Dispatch, Reducer } from '@reduxjs/toolkit';

type SettersFor<U> = {
  [K in keyof U]: U[K] extends Array<infer T>
  ? { set: (value: T[] | ((prev: T[]) => T[])) => void } & Array<SettersFor<T>>
  : U[K] extends object
  ? { set: (value: U[K] | ((prev: U[K]) => U[K])) => void } & SettersFor<U[K]>
  : { set: (value: U[K] | ((prev: U[K]) => U[K])) => void };
};

type ReturnSlice<T> = {
  setters: SettersFor<T>,
  reducer: Reducer<T>
}

export const createReduxSlice = <T extends object>(
  sliceName: string,
  initialState: T,
  dispatch: Dispatch<any>
) : ReturnSlice<T> => {


const generateSetters = (obj: T, path: string[] = [], prefix = ''): SettersFor<T> => {
  const setters: any = {};
  if (!obj) return setters;

  const actions: any = {};

  Object.keys(obj).forEach((key) => {
    const value = obj[key];
    const currentPath = path.concat([key]);
    const actionType = `${prefix}${key}`;

    if (Array.isArray(value)) {
      actions[actionType] = (newValue: any) => ({
        type: `${prefix}${key}`,
        payload: { path: currentPath, value: newValue },
      });
      setters[key] = {
        set: (newValue: any) => dispatch(actions[actionType](newValue)),
        ...generateSetters(value, currentPath, `${actionType}.`),
      };
    } else if (typeof value === 'object') {
      actions[actionType] = (newValue: any) => ({
        type: `${prefix}${key}`,
        payload: { path: currentPath, value: newValue },
      });
      setters[key] = {
        set: (newValue: any) => dispatch(actions[actionType](newValue)),
        ...generateSetters(value, currentPath, `${actionType}.`),
      };
    } else {
      actions[actionType] = (newValue: any) => ({
        type: `${prefix}${key}`,
        payload: { path: currentPath, value: newValue },
      });
      setters[key] = {
        set: (newValue: any) => dispatch(actions[actionType](newValue)),
      };
    }
  });

  setters.actions = bindActionCreators(actions, dispatch);

  return setters as SettersFor<T>;
};

  const slice = createSlice({
    name: sliceName,
    initialState,
    reducers: {},
    extraReducers: (builder) => {
      builder.addDefaultCase((state, action) => {
        const type = action?.type?.split('/')?.[0];
        if (type !== sliceName) return state;
        const path = action?.type?.split('/')?.[1];
        const value = action?.payload?.value;
        const pathParts = path?.split('.');
        // console.log({ state, action, path, value })
        let currentLevel = state;
        for (let i = 0; i < pathParts.length - 1; i++) {
          currentLevel = currentLevel[pathParts[i]];
        }
        const lastKey = pathParts[pathParts.length - 1];
        const currentValue = currentLevel[lastKey];

        if (typeof value === 'function') {
          currentLevel[lastKey] = value(currentValue);
        } else {
          currentLevel[lastKey] = value;
        }
      });
    },
  });

  return {
    reducer: slice.reducer,
    setters: generateSetters(initialState, [], `${sliceName}/`),
  } as ReturnSlice<T>;
};
store/InventorySlice.tsx
import { createReduxSlice } from '@/hooks/createReduxSlice';

const initialState = {
  tempCreateFilter: {},
  bulkUpdate: {
    selectedRows: [],
    attrsForUpdate: []
  },
 viewDetails: {},
  filter: {
    topLevelInventoryItem: 'Operating Systems',
    childLevelInventoryItem: 'Child Operating Systems',
    isCaseSensitive: false,
    isMatchAll: true,
    crieteria: {
      newItem: {} as any,
      added: [] as any
    },
  },
  singleEdit:{
    category: '',
    open: false,
  }
}

export const InventorySlice = (sliceName, dispatch) => (
  createReduxSlice(sliceName, initialState, dispatch)
)
store/index.jsx
import { bindActionCreators, combineReducers, configureStore } from '@reduxjs/toolkit';
import { useSelector } from 'react-redux';
import { InventorySlice } from './slices/InventorySlice';
import { ThemeSlice } from './slices/ThemeSlice';

type RootState = ReturnType<typeof reduxStore.getState>

const BindActionCreator =(slice,dispatch)=> {
  slice.setters.actions = bindActionCreators(slice.setters.actions, dispatch);
}

export const reduxStore = configureStore({
  reducer: {} as typeof rootReducer,
  middleware: (getDefaultMiddleware) => getDefaultMiddleware({serializableCheck: false}),
});

// 1.1) CREATE THE SLICES
const inventorySlice = InventorySlice('InventorySlice',reduxStore.dispatch)
const themeSlice = ThemeSlice('ThemeSlice',reduxStore.dispatch)

// 1.2) COMBINE THE SLICES
const rootReducer = combineReducers({
  InventorySlice: inventorySlice.reducer,
  ThemeSlice: themeSlice.reducer,
});

reduxStore.replaceReducer(rootReducer);

// 1.3) BIND THE ACTION CREATORS
BindActionCreator(inventorySlice,reduxStore.dispatch);
BindActionCreator(themeSlice,reduxStore.dispatch);

// 1.4) EXPORT THE STORES
export const inventoryStore = () => ({
  inventory: useSelector((state: RootState) => state.InventorySlice),
  inventoryActions: inventorySlice.setters
})

export const themeStore = () => ({
  theme: useSelector((state: RootState) => state.ThemeSlice),
  themeActions:  themeSlice.setters
})

Example usage

const {inventoryState, inventoryActions} = inventoryStore()

useEffect(() => {
  inventoryActions.filter.crieteria.newItem.set(state);
}, [state])
PreviousGlobalContext ApiNexttemp-practice

Last updated 1 year ago

Was this helpful?