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
相关产品推荐
相关产品推荐

