skip to Main Content

I got Next.js application with Typescript and I’m trying to make redux work with redux-persist, if the state in slice is same as the initial state, the application works fine, but if it actually have to persist some state I get an hydration errors:

  • Hydration failed because the initial UI does not match what was rendered on the server.
  • An error occurred during hydration. The server HTML was replaced with client content in <#document>.
  • Hydration failed because the initial UI does not match what was rendered on the server.

Also getting this error on server:
Error storing data ReferenceError: window is not defined

Even though the application works as intended even when there are these errors occuring I would like to solve them, so it doesn’t look this messy, but I’m pretty lost on why this is happening.

Here is my trimmed code for redux set up:

slice.ts

import { InquiryCartState } from "@/types/InquiryCart";
import { createSlice } from "@reduxjs/toolkit";

const initialState: InquiryCartState = {
  products: []
};

const inquiryCartSlice = createSlice({
  name: "inquiryCart",
  initialState,
  reducers: {
  },
});

export default inquiryCartSlice.reducer;

rootReducer.ts

import { combineReducers } from "@reduxjs/toolkit";
import inquiryCartReducer from "./slice";
import AsyncStorage from '@react-native-async-storage/async-storage';
import { persistReducer } from "redux-persist";

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

export const rootReducer = combineReducers({
  inquiryCart: persistReducer(persistConfig, inquiryCartReducer),
})

store.ts

import { configureStore } from "@reduxjs/toolkit";
import { rootReducer } from "./rootReducer";

export const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({ serializableCheck: false })
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

ReduxProvider.ts

"use client"

import { Provider } from "react-redux";
import { ReactNode } from "react";
import { store } from "@/lib/store";
import { persistStore } from "redux-persist";

persistStore(store)
export default function ReduxProvider({ children }: { children: ReactNode }) {
  return <Provider store={store}>{children}</Provider>;
}

2

Answers


  1. The server has no access to your persisted data, so SSR will render whatever would be rendered for a newly created Redux store, while your Client will render depending on the persisted data.

    There is no way of telling the server what your user has persisted, so the best you can do is either live with this or disable persistance.

    Login or Signup to reply.
  2. I add this in to my store.js file where I defined storage in rootPersistConfig.

    store.ts

    const isClient = typeof window !== "undefined";
    const createNoopStorage = () => {
      return {
        getItem() {
          return Promise.resolve(null as string  |null);
        },
        setItem(_key: string, value: number) {
          return Promise.resolve(value);
        },
        removeItem() {
          return Promise.resolve();
        },
      };
    };
    const storage =
      isClient
        ? createWebStorage("local")
        : createNoopStorage();
        
    const rootPersistConfig = {
      key: "root",
      storage : isClient? storage : createNoopStorage(),
      whitelist: ["auth", "navbar"],
    };
    const persistedReducer = persistReducer(rootPersistConfig, rootReducer);
    
    
    export const store = configureStore({
      reducer: persistedReducer,
      middleware: (getDefaultMiddleware) =>
        getDefaultMiddleware({ serializableCheck: false }),
    });
    
    export const persister = persistStore(store);
    export function getReduxState() {
    
    
    return store.getState();
    
    }
    
    export type App_state = ReturnType<typeof store.getState>;
    export type AppDispatch = typeof store.dispatch;
    const useAppDispatch:() => AppDispatch = useDispatch;
    export const useAppSelector: TypedUseSelectorHook<App_state> = useSelector;
    export {useAppDispatch};
    

    And in ReduxProvider.tsx I provide store with persistor

    ReduxProvider.tsx

    "use client";
    
    import { Provider } from "react-redux";
    import { PersistGate } from "redux-persist/integration/react";
    import { persister, store } from "./store";
    
    export default function ReduxProvider({
      children
    }: {
      children: React.ReactNode;
    }) {
      return (
        <Provider store={store}>
          <PersistGate loading={<FallbackLoader />} persistor={persister}>
            {children}
          </PersistGate>
        </Provider>
      );
    }
    

    With above code, I don’t face error of hydration error because of ReduxProvider

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search