Next.js+Redux-Toolkit从localStorage初始化数据遇未定义错误
解决Next.js+Redux-Toolkit初始化时localStorage未定义的问题
问题场景
在Next.js项目中使用Redux-Toolkit从localStorage初始化认证状态时,页面生成阶段抛出错误:ReferenceError: localStorage is not defined。相关代码如下:
import { createSlice } from "@reduxjs/toolkit"; const initialIsAuthenticated = localStorage.getItem("isAuthenticated") != null ? JSON.parse(localStorage.getItem("isAuthenticated")) : null; const initialToken = localStorage.getItem("token") != null ? JSON.parse(localStorage.getItem("token")) : null; const initialType = localStorage.getItem("type") != null ? JSON.parse(localStorage.getItem("type")) : null; const initialBilkentId = localStorage.getItem("bilkentId") != null ? JSON.parse(localStorage.getItem("bilkentId")) : null; const initialAuthState = { isAuthenticated: initialIsAuthenticated, token: initialToken, type: initialType, bilkentId: initialBilkentId, }; const setItemFunc = (isAuthenticated, token, type, bilkentId) => { localStorage.setItem("isAuthenticated", JSON.stringify(isAuthenticated)); localStorage.setItem("token", JSON.stringify(token)); localStorage.setItem("type", JSON.stringify(type)); localStorage.setItem("bilkentId", JSON.stringify(bilkentId)); }; const removeItem = () => { localStorage.removeItem("isAuthenticated"); localStorage.removeItem("token"); localStorage.removeItem("type"); localStorage.removeItem("bilkentId"); }; const authSlice = createSlice({ name: "authentication", initialState: initialAuthState, reducers: { login(state, action) { state.isAuthenticated = true; state.token = action.payload.token; state.type = action.payload.type; state.bilkentId = action.payload.bilkentId; setItemFunc( state.isAuthenticated, state.token, state.type, state.bilkentId ); }, logout(state) { state.isAuthenticated = false; state.token = null; state.type = null; state.bilkentId = null; removeItem(); }, }, }); export const authActions = authSlice.actions; export default authSlice.reducer;

问题原因
Next.js默认采用SSR(服务端渲染)或SSG(静态站点生成)模式,页面生成过程在Node.js环境中执行。而localStorage是浏览器专属的API,Node.js环境中不存在这个对象,因此直接在slice初始化时访问会触发未定义错误。
解决方案
方法1:环境判断动态生成初始状态
在获取localStorage前先判断当前是否处于浏览器环境(即window对象是否存在),仅在浏览器环境下读取localStorage数据,服务端环境返回默认初始值。
修改后的代码如下:
import { createSlice } from "@reduxjs/toolkit"; // 动态获取初始状态,区分服务端和客户端环境 const getInitialAuthState = () => { // 服务端环境直接返回默认空状态 if (typeof window === "undefined") { return { isAuthenticated: null, token: null, type: null, bilkentId: null, }; } // 客户端环境读取localStorage return { isAuthenticated: JSON.parse(localStorage.getItem("isAuthenticated")) ?? null, token: JSON.parse(localStorage.getItem("token")) ?? null, type: JSON.parse(localStorage.getItem("type")) ?? null, bilkentId: JSON.parse(localStorage.getItem("bilkentId")) ?? null, }; }; const setItemFunc = (isAuthenticated, token, type, bilkentId) => { // 仅在客户端执行localStorage操作 if (typeof window !== "undefined") { localStorage.setItem("isAuthenticated", JSON.stringify(isAuthenticated)); localStorage.setItem("token", JSON.stringify(token)); localStorage.setItem("type", JSON.stringify(type)); localStorage.setItem("bilkentId", JSON.stringify(bilkentId)); } }; const removeItem = () => { // 仅在客户端执行localStorage操作 if (typeof window !== "undefined") { localStorage.removeItem("isAuthenticated"); localStorage.removeItem("token"); localStorage.removeItem("type"); localStorage.removeItem("bilkentId"); } }; const authSlice = createSlice({ name: "authentication", initialState: getInitialAuthState(), reducers: { login(state, action) { state.isAuthenticated = true; state.token = action.payload.token; state.type = action.payload.type; state.bilkentId = action.payload.bilkentId; setItemFunc( state.isAuthenticated, state.token, state.type, state.bilkentId ); }, logout(state) { state.isAuthenticated = false; state.token = null; state.type = null; state.bilkentId = null; removeItem(); }, }, }); export const authActions = authSlice.actions; export default authSlice.reducer;
方法2:客户端初始化Redux状态
如果需要确保服务端和客户端状态一致,可以在客户端组件中通过useEffect触发Redux action,从localStorage加载初始状态:
- 在slice中添加初始化状态的action:
const authSlice = createSlice({ // ...其他配置 reducers: { // ...login、logout initializeAuth(state, action) { state.isAuthenticated = action.payload.isAuthenticated; state.token = action.payload.token; state.type = action.payload.type; state.bilkentId = action.payload.bilkentId; }, }, }); export const { login, logout, initializeAuth } = authSlice.actions;
- 在
_app.js或根组件中执行初始化:
import { useEffect } from 'react'; import { useDispatch } from 'react-redux'; import { initializeAuth } from '../store/authSlice'; function MyApp({ Component, pageProps }) { const dispatch = useDispatch(); useEffect(() => { const authData = { isAuthenticated: JSON.parse(localStorage.getItem("isAuthenticated")) ?? null, token: JSON.parse(localStorage.getItem("token")) ?? null, type: JSON.parse(localStorage.getItem("type")) ?? null, bilkentId: JSON.parse(localStorage.getItem("bilkentId")) ?? null, }; dispatch(initializeAuth(authData)); }, [dispatch]); return <Component {...pageProps} />; } export default MyApp;
此时slice的初始状态可以直接设为默认空值,因为客户端会在组件挂载后主动加载localStorage数据。
内容的提问来源于stack exchange,提问作者hasatserinkan
相关产品推荐
相关产品推荐

