<main>

React-redux

How to keep state using react-redux alongside redux-persist

The hooks used access and modify state are…

import { useSelector, useDispatch } from "react-redux";

Before using these you will need to set up your reducer with createSlice:

The practice i follow is to store this in a folder named “state” and create an index.js file.

import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  author: "bob",
  content: "i like salad",
};

export const stateSlice = createSlice({
  name: "article",
  initialState,
  reducers: {
    setArticle: (state, action) => {
      (state.author = action.payload.author);
      (state.content = action.payload.content);
    },
  },
});

export const { setArticle } = stateSlice.actions;
export default stateSlice.reducer;

As you can see above, the initial state is set to an object with an example string for author & content. Below that is your stateSlice which will include your initial state, and your reducer functions. The setArticle reducer takes in two params, state and action. State refers to the current/previous state (initially it will === initialState) and action returns the new state. We then export the reducer function setArticle and stateSlice.

The next step is to set up our storage:

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";
import { Provider } from "react-redux";
import { configureStore } from "@reduxjs/toolkit";
import stateReducer from "./state";

const store = configureStore({
  reducer: {
    article: stateReducer,
  }
});

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <Provider store={store}>      
        <App />
    </Provider>
  </React.StrictMode>
);

useSelector is used to get the current value of your state variables. Heres an example:

const article = useSelector((state) => state.article);
// article === {author: "bob", content: "i like salad"}

In this case, as we haven’t changed anything, article will equal our initial state. .article refers to the name in your slice.

To change the state we will use useDispatch. Firstly we need to import the reducer function and useDispatch:

import { useSelector, useDispatch } from "react-redux";
import { setArticle } from "../state";

const homePage = () => {
    const dispatch = useDispatch();
    const article = useSelector((state) => state.article);
    //article === {author: "bob", content: "i like salad"}
    //then we can change the article stored inside state with out setArticle reducer.
    dispatch(setArticle({author: "fred", content: "i do not like salad"}))
    const newArticle = useSelector((state) => state.article);
    //newArticle === {author: "fred", content: "i do not like salad"}
}

To start off we declare dispatch, then article returned our current state, dispatch(setArticle) then changed our state, and finally we fetched our updated state with newArticle.

This will all work as expected to store variables to pass to different components and pages, as to store things such as user tokens and admin validation etc. But a problem i ran into was once you refresh the page.

Once the page is refreshed your state will be refreshed, which is not ideal. A solution to this is redux-persist. This will store your state on the webserver and be persistant even if you refresh the page. To implement this update your code as follows:

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";
import { Provider } from "react-redux";
import { configureStore } from "@reduxjs/toolkit";
import stateReducer from "./state";
import storage from "redux-persist/lib/storage";
import { PersistGate } from "redux-persist/integration/react";
import {
  persistStore,
  persistReducer,
  FLUSH,
  REHYDRATE,
  PAUSE,
  PERSIST,
  PURGE,
  REGISTER,
} from "redux-persist";
  
const persistConfig = { key: "root", storage, version: 1 };
const persistedReducer = persistReducer(persistConfig, stateReducer);
const store = configureStore({
  reducer: {
    article: persistedReducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
      },
    }),
});
  
ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistStore(store)}>
        <App />
      </PersistGate>
    </Provider>
  </React.StrictMode>
);

It looks like a lot but it’s just reconfiguring your storage to a sort of webStorage. I found i had to add the middleware to avoid some errors in the console as it was attempting actions and was throwing some typeErrors. We also wrap <App /> in a persistgate with persistStore of store, this will mean that when you close the webpage, refresh the webpage etc… your state will ‘persist’. the only thing that would refresh this is a reboot of your website, or alternitavely as this type of state is popularly used for authentication, you could add a reducer to log someone in and set their token so that they may access user features, and have a reducer to set the token to null when the user clicks log out.

</main>