Skip to content

Commit

Permalink
feat(persistInStorage): add redux-persist and retrieve from localStorage
Browse files Browse the repository at this point in the history
  • Loading branch information
Clm-Roig committed Mar 16, 2022
1 parent cfe988d commit c2a2c2e
Show file tree
Hide file tree
Showing 11 changed files with 87 additions and 115 deletions.
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"react-redux": "^7.2.6",
"react-router-dom": "^6.2.2",
"react-scripts": "5.0.0",
"redux-persist": "^6.0.0",
"typescript": "~4.1.5",
"uuid": "^8.3.2"
},
Expand Down
21 changes: 21 additions & 0 deletions src/app/FullScreenLoading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Box, CircularProgress, Typography } from '@mui/material';

function FullScreenLoading() {
return (
<Box
sx={{
display: 'flex',
alignItems: 'center',
flexDirection: 'column',
height: '100vh',
justifyContent: 'center'
}}>
<Typography variant="h4" align="center">
Chargement des données...
</Typography>
<CircularProgress size={50} sx={{ mt: 4 }} />
</Box>
);
}

export default FullScreenLoading;
11 changes: 1 addition & 10 deletions src/components/trackerList/TrackerList.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
import { Box, CircularProgress, Typography } from '@mui/material';
import { useEffect } from 'react';

import { useAppSelector, useAppDispatch } from '../../app/hooks';
import { useAppSelector } from '../../app/hooks';
import SliceStatus from '../../models/SliceStatus';
import { fetchAllTrackers } from '../../store/trackers/trackersSlice';
import selectTrackers from '../../store/trackers/trackers.selectors';

import TrackerCard from '../TrackerCard/TrackerCard';

function TrackerList() {
const { status, trackers } = useAppSelector(selectTrackers);
const dispatch = useAppDispatch();

useEffect(() => {
if (trackers === undefined) {
dispatch(fetchAllTrackers());
}
}, [trackers]);

return (
<Box>
Expand Down
12 changes: 8 additions & 4 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@ import ReactDOM from 'react-dom';
import './index.css';
import App from './app/App';
import { BrowserRouter } from 'react-router-dom';
import { store } from './store/store';
import { store, persistor } from './store/store';
import { Provider } from 'react-redux';
import * as serviceWorker from './serviceWorker';
import { PersistGate } from 'redux-persist/integration/react';
import FullScreenLoading from './app/FullScreenLoading';

ReactDOM.render(
<StrictMode>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
<PersistGate loading={<FullScreenLoading />} persistor={persistor}>
<BrowserRouter>
<App />
</BrowserRouter>
</PersistGate>
</Provider>
</StrictMode>,
document.getElementById('root')
Expand Down
12 changes: 4 additions & 8 deletions src/store/createTestStore.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counter/counterSlice';
import trackersReducer from './trackers/trackersSlice';
import { configureStore, EnhancedStore } from '@reduxjs/toolkit';
import rootReducer from './rootReducer';

export function createTestStore() {
export function createTestStore(): EnhancedStore {
const store = configureStore({
reducer: {
counter: counterReducer,
trackers: trackersReducer
}
reducer: rootReducer
});
return store;
}
10 changes: 10 additions & 0 deletions src/store/rootReducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { combineReducers } from '@reduxjs/toolkit';
import counterReducer from './counter/counterSlice';
import trackersReducer from './trackers/trackersSlice';

const rootReducer = combineReducers({
counter: counterReducer,
trackers: trackersReducer
});

export default rootReducer;
34 changes: 28 additions & 6 deletions src/store/store.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,36 @@
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
import counterReducer from './counter/counterSlice';
import trackersReducer from './trackers/trackersSlice';
import {
persistStore,
persistReducer,
REHYDRATE,
FLUSH,
PAUSE,
PERSIST,
PURGE,
REGISTER
} from 'redux-persist';
import storage from 'redux-persist/lib/storage'; // defaults to localStorage for web
import rootReducer from './rootReducer';

const persistConfig = {
key: 'root',
storage
};

const persistedReducer = persistReducer(persistConfig, rootReducer);

export const store = configureStore({
reducer: {
counter: counterReducer,
trackers: trackersReducer
}
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER] // redux-persist actions are not serializable, ignore them
}
})
});

export const persistor = persistStore(store);

export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
export type AppThunk<ReturnType = void> = ThunkAction<
Expand Down
49 changes: 1 addition & 48 deletions src/store/trackers/trackers.actions.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,2 @@
import { subDays } from 'date-fns';
import Tracker from '../../models/Tracker';
import TrackerStatus from '../../models/TrackerStatus';
import { v4 as uuidv4 } from 'uuid';

class TrackersActions {
// A mock function to mimic making an async request for data
fetchAll() {
return new Promise<{ data: Tracker[] }>((resolve) =>
setTimeout(
() =>
resolve({
data: [
{
id: uuidv4(),
beginDate: new Date().toString(),
name: "Boire plus d'eau",
defaultQuantity: 1,
status: TrackerStatus.active,
unit: "litre d'eau",
entries: []
},
{
id: uuidv4(),
beginDate: new Date().toString(),
defaultQuantity: 1,
name: 'Faire du yoga',
status: TrackerStatus.active,
unit: 'séance',
entries: []
},
{
id: uuidv4(),
beginDate: subDays(new Date(), 7).toString(),
defaultQuantity: 15,
duration: 45,
name: 'Faire des pompes',
status: TrackerStatus.active,
unit: 'pompes',
entries: []
}
]
}),
900
)
);
}
}
class TrackersActions {}
export default new TrackersActions();
19 changes: 1 addition & 18 deletions src/store/trackers/trackersSlice.spec.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,12 @@
import SliceStatus from '../../models/SliceStatus';
import trackersReducer, { TrackersState, fetchAllTrackers } from './trackersSlice';
import trackersReducer from './trackersSlice';

describe('counter reducer', () => {
const initialState: TrackersState = {
error: {},
status: SliceStatus.idle,
trackers: undefined
};
it('should handle initial state', () => {
expect(trackersReducer(undefined, { type: 'unknown' })).toEqual({
error: {},
status: SliceStatus.idle,
trackers: undefined
});
});

it('should handle fetch all trackers pending', () => {
const actual = trackersReducer(initialState, fetchAllTrackers.pending);
expect(actual.status).toEqual(SliceStatus.loading);
expect(actual.error).toEqual({});
});

it('should handle fetch all trackers error', () => {
const actual = trackersReducer(initialState, fetchAllTrackers.rejected);
expect(actual.status).toEqual(SliceStatus.failed);
expect(actual.error).not.toEqual({});
});
});
28 changes: 7 additions & 21 deletions src/store/trackers/trackersSlice.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { createAsyncThunk, createSlice, SerializedError } from '@reduxjs/toolkit';
import { createSlice, SerializedError } from '@reduxjs/toolkit';

import TrackersActions from './trackers.actions';
import Tracker from '../../models/Tracker';
import SliceStatus from '../../models/SliceStatus';

Expand All @@ -20,33 +19,20 @@ const initialState: TrackersState = {

// ===== Thunk

export const fetchAllTrackers = createAsyncThunk('trackers/fetchAllTrackers', async () => {
const response = await TrackersActions.fetchAll();
return response.data;
});
// export const fetchAllTrackers = createAsyncThunk('trackers/fetchAllTrackers', async () => {
// const response = await TrackersActions.fetchAll();
// return response.data;
// });

// ===== Reducers

export const trackersSlice = createSlice({
name: 'trackers',
initialState,
reducers: {},
reducers: {}
// The `extraReducers` field lets the slice handle actions defined elsewhere,
// including actions generated by createAsyncThunk or in other slices.
extraReducers: (builder) => {
builder
.addCase(fetchAllTrackers.pending, (state) => {
state.status = SliceStatus.loading;
})
.addCase(fetchAllTrackers.fulfilled, (state, action) => {
state.status = SliceStatus.idle;
state.trackers = action.payload;
})
.addCase(fetchAllTrackers.rejected, (state, action) => {
state.status = SliceStatus.failed;
state.error = action.error;
});
}
// extraReducers: () => {}
});

export default trackersSlice.reducer;

0 comments on commit c2a2c2e

Please sign in to comment.