Home > Mobile >  Getting localStorage is not defined in Next.js for initial state
Getting localStorage is not defined in Next.js for initial state

Time:02-03

I am creating a project in Next.js with Redux Toolkit. I tried to use localStorage and got the error 'localStorage is not defined', so I moved to cookies-next but here also getting a problem. Kindly check my themeSlice page:

import { createSlice } from "@reduxjs/toolkit"
import { getCookie, hasCookie, setCookie } from 'cookies-next'
const initialState = {
    name: hasCookie('theme')?getCookie('theme'):'light'
}
export const Theme = createSlice({
    name: "theme",
    initialState,
    reducers: {
        toggleTheme: (state, action) => {
            if(state.name === "light") {
                state.name = "dark"
                setCookie('theme', 'dark')
            } else {
                state.name = "light"
                setCookie('theme', 'light')
            }
        }
    }
})
export const {toggleTheme} = Theme.actions
export default Theme.reducer

Here in initialState, I am getting 'light' every time, even cookies getting set to dark with reducer. The problem is getCookie is not working in initialState. It returns undefined every time.

CodePudding user response:

I wouldn't use cookies for storing a theme, which is only a client thing. Use localStorage and dynamic with no SSR to avoid the "localStorage is not defined" error.

To dynamically load a component on the client side, you can use the ssr option to disable server rendering. This is useful if an external dependency or component relies on browser APIs like window.

import { createSlice } from "@reduxjs/toolkit";
const initialState = {
  name: localStorage.getItem("theme") ? localStorage.getItem("theme") : "light",
};
export const Theme = createSlice({
  name: "theme",
  initialState,
  reducers: {
    toggleTheme: (state, action) => {
      if (state.name === "light") {
        state.name = "dark";
        localStorage.setItem("theme", "dark");
      } else {
        state.name = "light";
        localStorage.setItem("theme", "light");
      }
    },
  },
});
export const { toggleTheme } = Theme.actions;
export default Theme.reducer;
import dynamic from "next/dynamic";

// use the correct path for import
const reducer = dynamic(() => import("./store"), { ssr: false });
const toggleTheme = dynamic(() =>
  import('./store').then((mod) => mod.toggleTheme)
)

function Home() {
  // you can use them here
  return <div></div>;
}

export default Home;

CodePudding user response:

This issue is likely because the getCookie function is being called before the cookie is set in the reducer. To fix this, you can use the useEffect hook in a Next.js component to retrieve the value of the cookie after it has been set:

import { useEffect, useState } from 'react';
import { getCookie } from 'cookies-next';

function YourComponent() {
  const [theme, setTheme] = useState('light');

  useEffect(() => {
    const cookie = getCookie('theme');
    if (cookie) {
      setTheme(cookie);
    }
  }, []);

  // ...
}

This way, the value of the theme state will be updated with the value of the cookie after it has been set.

And the code seems fine, however, there are a few optimizations that can be made:

Use object destructuring in the initialState:

const initialState = {
    name: hasCookie('theme') ? getCookie('theme') : 'light',
};

Instead of using an if-else statement in the toggleTheme reducer, you can use a ternary operator:

toggleTheme: (state) => {
    state.name = state.name === 'light' ? 'dark' : 'light';
    setCookie('theme', state.name);
},

You can also use the spread operator to create a new object instead of mutating the existing state:

toggleTheme: (state) => {
    const newState = {
        ...state,
        name: state.name === 'light' ? 'dark' : 'light'
    };
    setCookie('theme', newState.name);
    return newState;
},

These optimizations will help you write clean and optimized code.

  • Related