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.