You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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加载初始状态:

  1. 在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;
  1. 在_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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 03:10:37