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

Next.js中ReferenceError: localStorage is not defined问题求助

Next.js迁移中localStorage未定义错误解决方案

将React项目迁移到Next.js,完成pages路由和store.js重构后,出现错误:ReferenceError: localStorage is not defined,错误发生在reducers/auth.js的initialState初始化阶段,具体代码位置如下:

19 | const initialState = {
>20 | access: localStorage.getItem('access'),
    |        ^
 21 | refresh: localStorage.getItem('refresh'),
 22 | isAuthenticated: null,
 23 | user: null

相关代码文件

reducers/auth.js

import {
    LOGIN_SUCCESS,
    LOGIN_FAIL,
    SIGNUP_SUCCESS,
    SIGNUP_FAIL,
    ACTIVATION_SUCCESS,
    ACTIVATION_FAIL,
    USER_LOADED_SUCCESS,
    USER_LOADED_FAIL,
    AUTHENTICATED_SUCCESS,
    AUTHENTICATED_FAIL,
    PASSWORD_RESET_SUCCESS,
    PASSWORD_RESET_FAIL,
    PASSWORD_RESET_CONFIRM_SUCCESS,
    PASSWORD_RESET_CONFIRM_FAIL,
    LOGOUT
} from '../actions/types';

const initialState = {
    access: localStorage.getItem('access'),
    refresh: localStorage.getItem('refresh'),
    isAuthenticated: null,
    user: null
};

export default function (state = initialState, action) {
    const { type, payload } = action;

    switch(type) {
        case AUTHENTICATED_SUCCESS:
            return {
                ...state,
                isAuthenticated: true
            }
        case LOGIN_SUCCESS:
            localStorage.setItem('access', payload.access);
            localStorage.setItem('refresh', payload.refresh);
            return {
                ...state,
                isAuthenticated: true,
                access: payload.access,
                refresh: payload.refresh
            }
        case USER_LOADED_SUCCESS:
            return {
                ...state,
                user: payload
            }
        case SIGNUP_SUCCESS:
            return {
                ...state,
                isAuthenticated: false
            }
        case AUTHENTICATED_FAIL:
            return {
                ...state,
                isAuthenticated: false
            }
        case USER_LOADED_FAIL:
            return {
                ...state,
                user: null
            }
        case LOGIN_FAIL:
        case SIGNUP_FAIL:
        case LOGOUT:
            localStorage.removeItem('access');
            localStorage.removeItem('refresh');
            return {
                ...state,
                access: null,
                refresh: null,
                isAuthenticated: false,
                user: null
            }
        case PASSWORD_RESET_SUCCESS:
        case PASSWORD_RESET_FAIL:
        case ACTIVATION_SUCCESS:
        case ACTIVATION_FAIL:
        case PASSWORD_RESET_CONFIRM_SUCCESS:
        case PASSWORD_RESET_CONFIRM_FAIL:
            return {
                ...state
            }

        default:
            return state
    }
};

src/store.js

import { useMemo } from 'react';
import { applyMiddleware } from 'redux';
import { legacy_createStore as createStore } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunkMiddleware from 'redux-thunk';
import reducers from './reducers';

let store;

function initStore(initialState) {
  return createStore(
    reducers,
    initialState,
    composeWithDevTools(applyMiddleware(thunkMiddleware))
  );
};

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

  if (preloadedState && store) {
    _store = initStore({
      ...store.getState(),
      ...preloadedState,
    })
    store = undefined;
  }

  if (typeof window === 'undefined') return _store;
  if (!store) store = _store;

  return _store;
}

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

问题原因

Next.js支持服务端渲染(SSR),在服务端执行代码时,浏览器的window对象和localStorage API不存在,直接调用会抛出未定义错误。

解决方案

方案一:修改initialState,添加环境判断

直接在reducers/auth.js的initialState中判断当前是否为客户端环境,仅在客户端时读取localStorage:

const initialState = {
    access: typeof window !== 'undefined' ? localStorage.getItem('access') : null,
    refresh: typeof window !== 'undefined' ? localStorage.getItem('refresh') : null,
    isAuthenticated: null,
    user: null
};

同时,为了避免其他action中操作localStorage时在服务端报错,给所有localStorage操作添加环境判断:

// LOGIN_SUCCESS案例
case LOGIN_SUCCESS:
    if (typeof window !== 'undefined') {
        localStorage.setItem('access', payload.access);
        localStorage.setItem('refresh', payload.refresh);
    }
    return {
        ...state,
        isAuthenticated: true,
        access: payload.access,
        refresh: payload.refresh
    }

// LOGIN_FAIL、SIGNUP_FAIL、LOGOUT案例
case LOGIN_FAIL:
case SIGNUP_FAIL:
case LOGOUT:
    if (typeof window !== 'undefined') {
        localStorage.removeItem('access');
        localStorage.removeItem('refresh');
    }
    return {
        ...state,
        access: null,
        refresh: null,
        isAuthenticated: false,
        user: null
    }

方案二:在store初始化时注入客户端localStorage值

这种方式更符合Next.js的SSR逻辑,服务端初始化store时不涉及localStorage,仅在客户端初始化时注入localStorage数据:

  1. 修改reducers/auth.js的initialState为默认值:
const initialState = {
    access: null,
    refresh: null,
    isAuthenticated: null,
    user: null
};
  1. 修改src/store.js的initializeStore函数,在客户端环境下读取localStorage并作为预加载状态传入:
export const initializeStore = (preloadedState) => {
  let clientPreloadedState = preloadedState;
  // 仅在客户端时获取localStorage数据
  if (typeof window !== 'undefined') {
    clientPreloadedState = {
      ...clientPreloadedState,
      auth: {
        access: localStorage.getItem('access'),
        refresh: localStorage.getItem('refresh'),
        isAuthenticated: null,
        user: null
      }
    };
  }

  let _store = store ?? initStore(clientPreloadedState);

  if (clientPreloadedState && store) {
    _store = initStore({
      ...store.getState(),
      ...clientPreloadedState,
    })
    store = undefined;
  }

  if (typeof window === 'undefined') return _store;
  if (!store) store = _store;

  return _store;
}

同样,action中的localStorage操作也需要添加环境判断,参考方案一的修改。

内容的提问来源于stack exchange,提问作者Moammer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 02:50:40