Skip to content

Commit 28a1e06

Browse files
chore: redux presist
Signed-off-by: rishabhsharma1997 <[email protected]>
1 parent c946aaa commit 28a1e06

File tree

8 files changed

+1400
-727
lines changed

8 files changed

+1400
-727
lines changed

package-lock.json

Lines changed: 1249 additions & 541 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@
4747
"@testing-library/react": "^14.1.2",
4848
"@types/jest": "^29.5.11",
4949
"@types/lodash": "^4.17.7",
50+
"@types/node": "^22.2.0",
5051
"@types/react": "^18.2.45",
5152
"@types/react-dom": "^18.2.18",
53+
"@types/react-redux": "^7.1.33",
5254
"@typescript-eslint/eslint-plugin": "^6.12.0",
5355
"@typescript-eslint/parser": "^6.12.0",
5456
"commitizen": "^4.3.0",
@@ -62,18 +64,18 @@
6264
"jest": "^29.7.0",
6365
"jest-environment-jsdom": "^29.7.0",
6466
"lint-staged": "^14.0.1",
65-
"lodash": "^4.17.21",
6667
"mui-datatables": "^4.3.0",
6768
"notistack": "^3.0.1",
6869
"prettier": "^3.0.3",
6970
"prettier-plugin-organize-imports": "^3.2.3",
7071
"react-error-boundary": "^4.0.12",
7172
"react-markdown": "^8.0.7",
72-
"react-redux": "^8.1.3",
73+
"react-redux": "^8.1.1",
74+
"redux": "^5.0.1",
7375
"rehype-raw": "^6.1.1",
7476
"remark-gfm": "^3.0.1",
7577
"ts-jest": "^29.1.1",
76-
"tsup": "^8.0.1",
78+
"tsup": "^8.2.4",
7779
"typescript": "^5.3.3"
7880
},
7981
"peerDependencies": {
@@ -82,12 +84,16 @@
8284
"@mui/material": "^5.15.11",
8385
"@types/mui-datatables": "*",
8486
"@xstate/react": "^4.1.1",
87+
"lodash": "^4.17.21",
8588
"mui-datatables": "*",
8689
"react": ">=17",
8790
"react-dom": ">=17",
8891
"xstate": "^5.13.0"
8992
},
9093
"peerDependenciesMeta": {
94+
"lodash": {
95+
"optional": true
96+
},
9197
"@emotion/react": {
9298
"optional": true
9399
},

src/custom/Stepper/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ interface CustomizedStepperPropsI {
2424
stepLabels: string[];
2525
children: React.ReactNode;
2626
icons: React.ComponentType<IconProps>[];
27-
ContentWrapper?: React.ComponentType;
27+
ContentWrapper?: React.ComponentType<{ children: React.ReactNode }>;
2828
}
2929

3030
interface UseStepperI {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
import { ThunkDispatch } from '@reduxjs/toolkit';
3+
import { FC, ReactNode, useEffect, useState } from 'react';
4+
import { useDispatch } from 'react-redux';
5+
import { Dispatch } from 'redux';
6+
import { RehydrateStateAction } from './initReduxPersist';
7+
8+
interface PersistedStateProviderProps {
9+
children: ReactNode;
10+
loadPersistedState: () => (dispatch: Dispatch<RehydrateStateAction>) => void;
11+
}
12+
13+
export const PersistedStateProvider: FC<PersistedStateProviderProps> = ({
14+
children,
15+
loadPersistedState
16+
}) => {
17+
const [loading, setLoading] = useState(true);
18+
const [error, setError] = useState<Error | null>(null);
19+
20+
const dispatch = useDispatch<ThunkDispatch<any, unknown, RehydrateStateAction>>();
21+
22+
useEffect(() => {
23+
if (!loading) {
24+
return;
25+
}
26+
try {
27+
dispatch(loadPersistedState());
28+
} catch (e) {
29+
setError(e as Error);
30+
}
31+
setLoading(false);
32+
}, [loading, dispatch, loadPersistedState]);
33+
34+
if (error) {
35+
console.error('Error Loading Persisted State', error);
36+
}
37+
38+
if (loading) {
39+
return null;
40+
}
41+
42+
return <>{children}</>;
43+
};

src/redux-persist/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './PersistedStateProvider';
2+
export * from './initReduxPersist';

src/redux-persist/index.tsx

Lines changed: 0 additions & 181 deletions
This file was deleted.

src/redux-persist/initReduxPersist.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import _ from 'lodash';
2+
import { Dispatch, Store } from 'redux';
3+
4+
const INVALID_REDUCER_PATH = Symbol('INVALID_REDUCER_PATH');
5+
const REHYDRATE_STATE_ACTION = 'REHYDRATE_STATE_ACTION';
6+
7+
export interface RehydrateStateAction {
8+
type: typeof REHYDRATE_STATE_ACTION;
9+
payload: {
10+
reducerPath: string;
11+
inflatedState: unknown;
12+
};
13+
}
14+
15+
type Action = RehydrateStateAction | { type: string; [key: string]: unknown };
16+
17+
interface ActionsToPersist {
18+
[actionType: string]: string[];
19+
}
20+
21+
const rehydrateState = (reducerPath: string, inflatedState: unknown): RehydrateStateAction => ({
22+
type: REHYDRATE_STATE_ACTION,
23+
payload: {
24+
reducerPath,
25+
inflatedState
26+
}
27+
});
28+
29+
const rehydrateStateReducer = (state: unknown, action: Action): unknown => {
30+
if (action.type === REHYDRATE_STATE_ACTION) {
31+
const appState = _.cloneDeep(state);
32+
_.set(
33+
appState as object,
34+
(action.payload as RehydrateStateAction['payload']).reducerPath.split('/'),
35+
(action.payload as RehydrateStateAction['payload']).inflatedState
36+
);
37+
return appState;
38+
}
39+
return state;
40+
};
41+
42+
export const initReduxPersist = (actionsToPersist: ActionsToPersist) => {
43+
const createPersistEnhancedReducer =
44+
(reducer: (state: unknown, action: Action) => unknown) =>
45+
(state: unknown, action: Action): unknown => {
46+
const newState = rehydrateStateReducer(state, action);
47+
return reducer(newState, action);
48+
};
49+
50+
const persistMiddleware =
51+
(store: Store) =>
52+
(next: (action: Action) => unknown) =>
53+
(action: Action): unknown => {
54+
const result = next(action);
55+
56+
const reducersToPersist = actionsToPersist[action.type];
57+
58+
if (reducersToPersist) {
59+
const appState = store.getState();
60+
reducersToPersist.forEach((reducerPath) => {
61+
const path = reducerPath.split('/');
62+
const stateToPersist = _.get(appState, path, INVALID_REDUCER_PATH);
63+
64+
if (stateToPersist === INVALID_REDUCER_PATH) {
65+
throw new Error(`Reducer Path to Persist Is Invalid: ${reducerPath}`);
66+
}
67+
68+
localStorage.setItem(reducerPath, JSON.stringify(stateToPersist));
69+
});
70+
}
71+
return result;
72+
};
73+
74+
const loadPersistedState = () => (dispatch: Dispatch<RehydrateStateAction>) => {
75+
Object.values(actionsToPersist).forEach((reducerPaths) => {
76+
reducerPaths.forEach((path) => {
77+
let inflatedState = localStorage.getItem(path);
78+
try {
79+
if (inflatedState) {
80+
inflatedState = JSON.parse(inflatedState);
81+
dispatch(rehydrateState(path, inflatedState));
82+
}
83+
} catch (e) {
84+
console.error(`Error rehydrating state for reducer ${path}`, inflatedState);
85+
}
86+
});
87+
});
88+
};
89+
90+
return {
91+
persistMiddleware,
92+
createPersistEnhancedReducer,
93+
loadPersistedState
94+
};
95+
};

tsup.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { defineConfig } from 'tsup';
44
const env = process.env.NODE_ENV;
55

66
export default defineConfig({
7-
outdir: 'dist',
7+
outDir: 'dist',
88
entry: ['src/index.tsx'],
99
bundle: env === 'production',
1010
clean: true,

0 commit comments

Comments
 (0)