Next.js中无法访问localStorage的问题求助
解决Next.js中localStorage未定义的终端报错
问题原因
即便加了"use client"标记,组件初始化时的useState初始值计算逻辑仍会在服务端渲染阶段执行,而服务端环境不存在localStorage API,因此终端会抛出ReferenceError;但浏览器客户端运行时正常,因为浏览器支持localStorage。
修复方案
将从localStorage读取初始值的逻辑移到useEffect中(useEffect仅在客户端执行),先给state设置默认值,组件挂载后再从localStorage加载实际值:
修改后的完整代码
"use client"; import React, { useEffect, useState } from "react"; import Entry from "./Entry"; import useEntryModal from "@/app/hooks/useEntryModal"; const MainGrid = () => { // 先设置默认初始值,避免服务端执行时触发报错 const [metricKg, setMetricKg] = useState(false); const [metricCm, setMetricCm] = useState(false); const [compactView, setCompactView] = useState(false); // 组件挂载后(仅客户端)从localStorage读取存储的配置 useEffect(() => { // 加入try-catch处理JSON解析异常,避免无效存储值导致组件崩溃 try { const savedMetricKg = localStorage.getItem("metricKg"); if (savedMetricKg !== null) { setMetricKg(JSON.parse(savedMetricKg)); } const savedMetricCm = localStorage.getItem("metricCm"); if (savedMetricCm !== null) { setMetricCm(JSON.parse(savedMetricCm)); } const savedCompactView = localStorage.getItem("compactView"); if (savedCompactView !== null) { setCompactView(JSON.parse(savedCompactView)); } } catch (err) { console.error("读取localStorage配置失败:", err); } }, []); let entry = { num: 0, date: "Apr 4th", weight: 52, // kg by default waist: 50, // cm by default hip: 30, // cm by default }; const entryModal = useEntryModal(); const toggleWeightUnit = () => { setMetricKg((prevMetricKg: any) => !prevMetricKg); }; const toggleLengthUnit = () => { setMetricCm((prevMetricCm: any) => !prevMetricCm); }; const toggleViewMode = () => { setCompactView((prevCompactView: any) => !prevCompactView); }; // 配置变更时同步保存到localStorage useEffect(() => { localStorage.setItem("metricKg", JSON.stringify(metricKg)); }, [metricKg]); useEffect(() => { localStorage.setItem("metricCm", JSON.stringify(metricCm)); }, [metricCm]); useEffect(() => { localStorage.setItem("compactView", JSON.stringify(compactView)); }, [compactView]); return ( <div className="flex flex-col w-[90%] mx-auto p-4 gap-0 text-white"> <div className="flex flex-col"> <h1 className="text-7xl milky-walky text-white text-center w-fit mx-auto jura my-6 mb-0 relative"> Entry History <button onClick={() => entryModal.onOpen()} className="absolute top-[50%] translate-y-[-50%] right-[-5rem] centrion" > + </button> </h1> <div className="flex space-x-2 gap-0"> <div className="m-6 flex gap-3 border-r-2 border-neutral-600 py-2 pr-8"> Lbs <label htmlFor="one"> <input id="one" type="checkbox" onClick={toggleWeightUnit} checked={metricKg} /> </label> Kgs </div> <div className="m-6 flex gap-3 border-r-2 border-neutral-600 py-2 pr-8"> Inch <label htmlFor="one"> <input id="one" type="checkbox" onClick={toggleLengthUnit} checked={metricCm} /> </label> Cm </div> {/* View mode toggle */} <div className="m-6 flex gap-3 py-2 pl-6"> Compact <label htmlFor="compactToggle"> <input id="compactToggle" type="checkbox" onClick={toggleViewMode} checked={!compactView} /> </label> Comfortable </div> </div> </div> {/* Apply gap based on the view mode */} <div className={` bg-neutral-800/75 flex flex-col ${ compactView ? " py-2" : "gap-0" }`} > <Entry entry={entry} metricKg={metricKg} compactView={compactView} metricCm={metricCm} /> <Entry entry={entry} metricKg={metricKg} compactView={compactView} metricCm={metricCm} /> <Entry entry={entry} metricKg={metricKg} compactView={compactView} metricCm={metricCm} /> </div> </div> ); }; export default MainGrid;
核心改动说明
- 初始
useState直接设置默认值(如false),避免服务端执行时访问localStorage - 新增空依赖的
useEffect,在组件客户端挂载后读取localStorage并更新state - 加入
try-catch捕获JSON解析错误,防止localStorage中存储的无效数据导致组件崩溃
内容的提问来源于stack exchange,提问作者Yevheniia Simaka
相关产品推荐
相关产品推荐

