# Redux Easy

{% code title="createReduxSlice.tsx" %}

```typescriptreact
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>;
};

```

{% endcode %}

{% code title="store/InventorySlice.tsx" %}

```typescriptreact
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)
)

```

{% endcode %}

{% code title="store/index.jsx" %}

```typescriptreact
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
})

```

{% endcode %}

Example usage

```javascriptreact
const {inventoryState, inventoryActions} = inventoryStore()

useEffect(() => {
  inventoryActions.filter.crieteria.newItem.set(state);
}, [state])
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://mohsin-ejaz.gitbook.io/redux/redux-easy.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
