如何修复Hydration失败与localStorage未定义的React组件问题?
问题根源
- 服务端渲染(SSR)阶段不存在
window和localStorage对象,直接调用会触发「localStorage is not defined」错误; - 用
typeof window !== 'undefined'判断时,服务端会使用默认初始值(比如空数组),但客户端初始化时会读取localStorage的数据,导致服务端渲染的初始UI和客户端hydrate时的UI内容不一致,触发Hydration失败错误。
解决方法
核心思路:把依赖客户端API的操作放到useEffect里执行——useEffect只会在客户端组件挂载后运行,不会影响服务端渲染的初始UI,从根源避免两端UI不匹配的问题。
具体代码修改步骤
- 给
useState设置服务端、客户端一致的初始值(比如空数组) - 用
useEffect在客户端挂载后,读取localStorage的数据并更新state - 点击收藏时,同时更新state和
localStorage,保证UI实时同步
错误代码示例(你的原有写法)
// 错误:在useState初始值里直接判断window,导致两端UI不一致 const [bookmarks, setBookmarks] = useState(() => { if (typeof window !== 'undefined') { return JSON.parse(localStorage.getItem('bookmarks')) || []; } return []; });
正确代码示例
import { useState, useEffect } from 'react'; function BookmarkComponent(props) { // 1. 初始值设为两端一致的空数组 const [bookmarks, setBookmarks] = useState([]); // 2. 客户端挂载后再读取localStorage更新state useEffect(() => { const savedBookmarks = localStorage.getItem('bookmarks'); if (savedBookmarks) { setBookmarks(JSON.parse(savedBookmarks)); } }, []); // 3. 收藏操作:同步更新state和localStorage const addToBookmark = () => { const newBookmarks = [...bookmarks, props.data]; setBookmarks(newBookmarks); localStorage.setItem('bookmarks', JSON.stringify(newBookmarks)); }; return ( <div> <button onClick={addToBookmark}>加入收藏</button> <ul> {bookmarks.map((item, index) => ( <li key={index}>{item.title}</li> ))} </ul> </div> ); }
额外优化点
如果要避免客户端hydrate时的UI闪烁,可以增加一个客户端加载状态,先显示占位内容:
const [isClientLoaded, setIsClientLoaded] = useState(false); useEffect(() => { setIsClientLoaded(true); // 读取localStorage的逻辑... }, []); return ( <div> {!isClientLoaded ? ( <p>加载中...</p> ) : ( // 渲染收藏列表和按钮 )} </div> );
内容的提问来源于stack exchange,提问作者Hary
相关产品推荐
相关产品推荐

