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

Next.js动态路由中获取LocalStorage数据失败的问题排查

解决Next.js动态路由中LocalStorage数据读取异常问题

问题核心

你遇到的问题本质是Next.js的**服务端渲染(SSR)**特性导致的:

  • 动态路由页面在服务端渲染阶段,window.localStorage根本不存在,所以初始渲染时countries只能是默认的空数组
  • 用useEffect读取LocalStorage是客户端行为,会在初始渲染之后执行,这就导致页面首次渲染时拿不到数据,访问country.name.nativeName自然会报错

修复步骤

1. 修正useLocalStorage钩子,兼容SSR

把你的自定义钩子改成下面这样,确保服务端渲染时不会报错,同时客户端能正确读取LocalStorage:

import { useState, useEffect } from 'react';

function useLocalStorage(key, initialValue) {
  // 服务端渲染时,初始值用传入的默认值,避免访问window报错
  const [storedValue, setStoredValue] = useState(initialValue);

  useEffect(() => {
    // 只在客户端执行读取操作
    try {
      const item = window.localStorage.getItem(key);
      // 如果LocalStorage有数据,就更新状态
      if (item) {
        setStoredValue(JSON.parse(item));
      }
    } catch (error) {
      console.error('读取LocalStorage失败:', error);
    }
  }, [key, initialValue]);

  // 封装存数据的方法
  const setValue = (value) => {
    try {
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.error('写入LocalStorage失败:', error);
    }
  };

  return [storedValue, setValue];
}

export default useLocalStorage;

2. 动态路由页面添加空值判断/加载状态

在/country/[countryName]页面里,不要直接访问country的属性,先判断数据是否存在:

import useLocalStorage from '../hooks/useLocalStorage';

export default function CountryPage({ params }) {
  const [countries] = useLocalStorage('countries', []);
  // 根据路由参数找到对应的国家
  const country = countries.find(c => c.name.common === params.countryName);

  // 数据未加载完成时,显示加载提示或空状态
  if (!country) {
    return <div>加载中...</div>;
    // 或者根据需求显示:<div>未找到该国家</div>
  }

  return (
    <div>
      <h1>{country.name.nativeName}</h1>
      {/* 其他国家信息 */}
    </div>
  );
}

3. 可选:预加载数据(优化体验)

如果希望动态路由页面能更快拿到数据,可以在首页请求API并存入LocalStorage时,同时把数据通过getStaticProps或者getServerSideProps传递给动态路由页面,但这样会失去LocalStorage缓存的意义,适合对首屏速度要求极高的场景:

// /country/[countryName].js
export async function getStaticProps({ params }) {
  // 这里重复请求API,或者从其他缓存(如Redis)获取
  const res = await fetch('https://your-api-url/countries');
  const countries = await res.json();
  const country = countries.find(c => c.name.common === params.countryName);

  return {
    props: { country }
  };
}

export async function getStaticPaths() {
  // 生成所有可能的路由路径
  const res = await fetch('https://your-api-url/countries');
  const countries = await res.json();
  const paths = countries.map(country => ({
    params: { countryName: country.name.common }
  }));

  return { paths, fallback: 'blocking' };
}

关键注意点

  • 永远不要在服务端渲染的代码中直接访问window、document等浏览器API,必须用useEffect或者typeof window !== 'undefined'做判断
  • 客户端状态更新是异步的,初始渲染时一定要处理空值情况,避免出现Cannot read properties of undefined报错

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 02:52:24