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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 02:35:03