rt2zz / redux-persist

persist and rehydrate a redux store
MIT License
12.94k stars 866 forks source link

redux toolkit createAsyncThunk response not persisted #1410

Open josephgodwinkimani opened 2 years ago

josephgodwinkimani commented 2 years ago

Description of issue

Scenario: User has loaded a page state with data (list of roles). Said user goes offline whilst in same page. He/she reloads same page. State is lost.

Question: How do I persist this state?

rolesSlice.js

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import { axiosCall } from "../../services/API";

// Define the initial state using that type
const initialState = {
  roles: [],
  loading: "",
};

// A function that accepts a Redux action type string and a callback function that should return a promise
export const getRoles = createAsyncThunk("roleSlice/getRoles", async (_, thunkAPI) => {
  try {
    // axios GET request (no credentials required)
    const response = await axiosCall("roles?");
    /**
     * response.data => {"name:" "Super Admin", "active": true}
     */
    return response.data;
  } catch (err) {
    // Use `err.response.data` as `action.payload` for a `rejected` action,
    // by explicitly returning it using the `rejectWithValue()` utility
    return thunkAPI.rejectWithValue(await err.response.data);
  }
});

export const roleSlice = createSlice({
  name: "roles",
  // `createSlice` will infer the state type from the `initialState` argument
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    // A builder provides addCase, addMatcher and addDefaultCase functions that may be called to define what actions this reducer will handle
    builder
      // Add a case reducer to handle a single exact action type
      .addCase(getRoles.pending, (state, action) => {
        state.loading = "pending";
      })
      .addCase(getRoles.fulfilled, (state, action) => {
        state.loading = "fulfilled";
        state.roles.push(action.payload);
      })
      .addCase(getRoles.rejected, (state, action) => {
        state.loading = "rejected";
      });
  },
});

export default roleSlice.reducer;

store.js

import { configureStore, combineReducers } from "@reduxjs/toolkit";
import { roleSlice } from "../app/features/roles/roleSlice";
import {
    persistStore,
    persistReducer,
    FLUSH,
    REHYDRATE,
    PAUSE,
    PERSIST,
    PURGE,
    REGISTER,
} from "redux-persist";
import storage from "redux-persist/lib/storage";

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

const rootReducers = combineReducers({
    roles: roleSlice.reducer,
});

const persistedReducer = persistReducer(persistConfig, rootReducers);

export const store = configureStore({
    reducer: persistedReducer,
    middleware: (getDefaultMiddleware) =>
        getDefaultMiddleware({
            serializableCheck: {
                ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
            },
        }),
});

export const persistor = persistStore(store);

components/Roles.js

import React, { useState, useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { unwrapResult } from "@reduxjs/toolkit";
import { getRoles } from "../../app/features/roles/roleSlice";
import { XDCTable } from "../../customComponents/XDCTable"

const Roles = () => {
    const dispatch = useDispatch(); 
    const { roles } = useSelector((state) => state.roles);
    const [data, setData] = useState([]);
    const RenderTable = () => {
        //return data.map((role) => {
        return Object.keys(data).map((key) => {
            const role = data[key];
            return (
                <tr key={key}>
                        <td>{role.name}</td>
                        <td>{role.status}</td>
                </tr>
            );
        });
    };

    useEffect(() => {
        const isOnline = window.navigator.onLine;
        // true or false

        if (isOnline) {
            dispatch(getRoles())
                .then(unwrapResult)
                .then((res) => {
                    console.log(res);
                    if (!res.someErrorMessagefromAPI) {
                        setData(res);
                    } else {
                        //some fn
                    }
                })
                .catch((e) => {
                    console.log(e);
                    //some fn
                });
        } else {
            setData(roles);
        }
    }, [dispatch]);

    return (
        <>
            <XDCTable>
                {RenderTable()}
            </XDCTable>         
        </>
    );
};

export default Roles;

index.js

import React from "react";
import ReactDOM from "react-dom/client";
import * as serviceWorkerRegistration from "./serviceWorkerRegistration";
import reportWebVitals from "./reportWebVitals";
import App from "./App";
import { persistor, store } from "./app/store";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
    <React.StrictMode>
        <Provider store={store}>
            <PersistGate loading={null} persistor={persistor}>
                <App />
            </PersistGate>
        </Provider>
    </React.StrictMode>
);
serviceWorkerRegistration.unregister();
reportWebVitals(console.log);

dependency information

react-redux: 8.0.2 @reduxjs/toolkit: 1.8.5 redux-persist: 6.0.0 redux: 4.2.0