使用Next.js搭建基于Axios的认证流程时遇到localStorage未定义错误的求助
解决Next.js中Server Error: ReferenceError: localStorage is not defined
这问题我在Next.js项目里踩过好几次!核心原因非常明确:Next.js的服务端渲染(SSR)阶段是在Node.js环境运行的,而localStorage是浏览器专属的API,Node.js里根本不存在这个对象。你现在的代码在reducer最顶部直接调用localStorage.getItem,服务端执行这段代码的时候自然就会抛出ReferenceError。
下面给你两种可行的解决方案,按需选择:
方案1:在reducer中判断客户端环境再读取localStorage
最简单的临时修复方式,就是在读取localStorage之前先判断当前是否处于浏览器环境:
import { LOGIN_SUCCESS, LOGIN_FAIL, LOGOUT } from "../actions/types"; // 只有在浏览器环境下才尝试读取localStorage const user = typeof window !== 'undefined' ? JSON.parse(localStorage.getItem("user")) : null; const initialState = user ? { isLoggedIn: true, user } : { isLoggedIn: false, user: null }; export default function (state = initialState, action) { const { type, payload } = action; switch (type) { case LOGIN_SUCCESS: return { ...state, isLoggedIn: true, user: payload.user, }; case LOGIN_FAIL: return { ...state, isLoggedIn: false, user: null, }; case LOGOUT: return { ...state, isLoggedIn: false, user: null, }; default: return state; } }
这种方式改动最小,但有个小缺点:reducer不再是纯函数,依赖了外部环境(浏览器的window对象),不符合Redux的设计原则。
方案2:在客户端组件中初始化状态(更推荐)
更规范的做法是让reducer保持纯函数,把读取localStorage的逻辑放到客户端组件里,再通过action同步到Redux store:
第一步:修改reducer的初始状态
把初始状态设为默认的未登录状态,让reducer不依赖任何外部环境:
import { LOGIN_SUCCESS, LOGIN_FAIL, LOGOUT } from "../actions/types"; // 纯初始状态,不依赖外部API const initialState = { isLoggedIn: false, user: null }; export default function (state = initialState, action) { const { type, payload } = action; switch (type) { case LOGIN_SUCCESS: return { ...state, isLoggedIn: true, user: payload.user, }; // 合并LOGIN_FAIL和LOGOUT的逻辑,减少重复代码 case LOGIN_FAIL: case LOGOUT: return { ...state, isLoggedIn: false, user: null, }; default: return state; } }
第二步:在根组件中读取localStorage并同步状态
在你的_app.js(或者项目的根布局组件)里,使用useEffect钩子(确保只在客户端执行)读取localStorage,然后dispatch action更新store:
import { useEffect } from 'react'; import { useDispatch } from 'react-redux'; // 假设你有一个loginSuccess的action,用来同步已登录用户状态 import { loginSuccess } from '../actions/authActions'; function MyApp({ Component, pageProps }) { const dispatch = useDispatch(); useEffect(() => { // useEffect的回调函数只会在客户端执行,安全访问localStorage const storedUser = localStorage.getItem('user'); if (storedUser) { try { const user = JSON.parse(storedUser); dispatch(loginSuccess({ user })); } catch (err) { // 处理解析失败的情况,比如localStorage里的内容损坏 localStorage.removeItem('user'); } } }, [dispatch]); return <Component {...pageProps} />; } export default MyApp;
这种方式更符合Redux的设计理念,同时还能避免服务端渲染和客户端hydration不匹配的问题,推荐使用。
内容的提问来源于stack exchange,提问作者Beckham
相关产品推荐
相关产品推荐

