2

I'm about to setup a new project using nextjs using the new app router, I could set redux toolkit up using a provider based on official example in nextjs repository : https://github.com/vercel/next.js/tree/canary/examples/with-redux but the problem is when I add redux-persist:

"use client";

import { PersistGate } from "redux-persist/integration/react";
/* Core */
import { Provider } from "react-redux";

/* Instruments */
import { persistor, reduxStore } from "@/lib/redux";

export const Providers = ({ children }) => {
  return (
    <Provider store={reduxStore}>
      <PersistGate persistor={persistor}>
        {children}
      </PersistGate>
    </Provider>
  );
};

also tried importing PersistGate using dynamic import:

"use client";

import dynamic from "next/dynamic";

const PersistGateComponent = dynamic(
  () =>
    import("redux-persist/integration/react").then((mod) => mod.PersistGate),
  {
    ssr: false,
  }
);

const SSROutset = ({ children, persistor }) => {
  if (typeof window !== "undefined") {
    return (
      <PersistGateComponent persistor={persistor}>
        {children}
      </PersistGateComponent>
    );
  }

  // You can create a visually hidden component to avoid layout flashes and also maintain SEO content
  // see Chakra for an example of one https://chakra-ui.com/docs/components/visually-hidden
  return (
    <span
      style={{
        border: "0",
        clip: "rect(0, 0, 0, 0)",
        height: "1px",
        width: "1px",
        margin: "-1px",
        padding: "0",
        overflow: "hidden",
        whiteSpace: "nowrap",
        position: "absolute",
      }}
    >
      {children}
    </span>
  );
};

export default SSROutset

and:

"use client";

/* Core */
import { Provider } from "react-redux";

/* Instruments */
import { persistor, reduxStore } from "@/lib/redux";
import SSROutset from "./redux/SSROutset";

export const Providers = ({ children }) => {
  return (
    <Provider store={reduxStore}>
      <SSROutset persistor={persistor}>
        {children}
      </SSROutset>
    </Provider>
  );
};

still getting following errors:

Hydration failed because the initial UI does not match what was rendered on the server.

1
  • 1
    I did it the same thing but I but children out of the persistGate Because when I have children in persist gate all page render client side not server side Commented Sep 9, 2023 at 15:49

3 Answers 3

0

I solved this problem with useEffect

const { list } = useAppSelector(state => state.example);

const [isMounted, setIsMounted] = useState(false);

useEffect(() => {
  setIsMounted(true);
}, []);

return (
  <div>
     {isMounted && list.length > 0 && ( ... )}
  </div>
)
Sign up to request clarification or add additional context in comments.

3 Comments

I don't see how is this relevant ?
I thought you were using data from the store in your components. And as I see you are getting a hydration error because the rendering on the client is different from rendering on the server. I got this error too and I decided to fix it by isMount flag in the state of component. I may be wrong on this matter, but it works
For this example I want to suggest using isMounted state in SSROutset: if (typeof window !== "undefined" && isMounted) {
0

I have work around this problem the store initial

import {
  addToCartMiddleware,
  updateCartMiddleware,
  deleteCartMiddleware,
} from "./slices/checkout/middleware";
import CheckoutLineReducer from "./slices/checkout/Line.Slice";

let store;

const rootReducer = combineReducers({
  notification: NotificationReducer,

});

const middlewares = [
  addToCartMiddleware,
  updateCartMiddleware,
  deleteCartMiddleware,
  thunk,
];

const persistConfig = {
  key: "primary",
  storage,
  whitelist: ["auth", "checkout"],
};

const persistedReducer = persistReducer(persistConfig, rootReducer);

function makeStore() {
  return configureStore({
    reducer: persistedReducer,
    devTools: process.env.NODE_ENV !== "production",
    middleware: middlewares,
  });
}

export const initializeStore = (preloadedState) => {
  let _store = store ?? makeStore();

  // For SSG and SSR always create a new store
  if (typeof window === "undefined") return _store;

  // Create the store once in the client
  if (!store) store = _store;

  return _store;
};

export function useStore() {
  const store = useMemo(() => initializeStore(), []);
  return store;
}

export default makeStore;

and after that I make Provider component to wrap the project in Layout

"use client";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import { persistStore } from "redux-persist";
import { useStore } from "@/lib/redux/store";

const ProviderWrapper = ({ children }) => {
  const store = useStore();
  let persistor;

  if (store) {
    persistor = persistStore(store, {}, function () {
      persistor.persist();
    });
  }

  return (
    <Provider store={store}>
      <PersistGate loading={<p>loading</p>} persistor={persistor} />
      {children}
    </Provider>
  );
};

export default ProviderWrapper;

the persist store working but it makes alot of action in the persist/PERSIST persist/REYHIDRATION

note: in Provider I put children out of PersistCate because when you add it inside the persist gate all the application will be client side

Comments

0
  1. create a custom storage
"use client";

import createWebStorage from "redux-persist/lib/storage/createWebStorage";

const createNoopStorage = () => {
  return {
    getItem(_key: any) {
      return Promise.resolve(null);
    },
    setItem(_key: any, value: any) {
      return Promise.resolve(value);
    },
    removeItem(_key: any) {
      return Promise.resolve();
    },
  };
};

const storage =
  typeof window !== "undefined"
    ? createWebStorage("local")
    : createNoopStorage();

export default storage;
  1. use this custom storage on your persistConfig:
import storage from "./customStorage";

// Redux persist config
const persistConfig = {
  key: "root",
  version: 1,
  storage,
};
  1. Remove persistgate from your storeProvider, no need to delay the app since we're not using react directly but next, thus next config.

If you are using react, wrap your root component with PersistGate. This delays the rendering of your app's UI until your persisted state has been retrieved and saved to redux (https://github.com/rt2zz/redux-persist?tab=readme-ov-file):

"use client";
import { AppStore, makeStore } from "@/lib/store";
import { setupListeners } from "@reduxjs/toolkit/query";
import type { ReactNode } from "react";
import { useEffect, useRef } from "react";
import { Provider } from "react-redux";
import { persistStore } from "redux-persist";

interface Props {
  readonly children: ReactNode;
}

export const StoreProvider = ({ children }: Props) => {
  const storeRef = useRef<AppStore | null>(null);
  if (!storeRef.current) {
    // Create the store instance the first time this renders
    storeRef.current = makeStore();
    persistStore(storeRef.current);
  }

  useEffect(() => {
    if (storeRef.current != null) {
      // configure listeners using the provided defaults
      // optional, but required for `refetchOnFocus`/`refetchOnReconnect` behaviors
      const unsubscribe = setupListeners(storeRef.current.dispatch);
      return unsubscribe;
    }
  }, []);
  return <Provider store={storeRef.current}>{children}</Provider>;
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.