React Hook useSyncExternalStore
一、核心定位
React 官方 Hook,用于安全订阅外部状态(如 localStorage、浏览器 API、全局变量、Rudex全局状态等),确保组件读取的外部状态始终一致,同时自动同步状态变化。
本质是通过 “订阅 - 发布模式” 实现与外部状态的同步,同时结合 React 内部的渲染调度机制,确保状态一致性。
二、核心参数(3 个,前 2 个必传)
| 参数名 | 作用 |
|---|---|
subscribe | 注册外部状态监听:传入 React 内部回调(状态变时通知 React),返回 “取消订阅” 函数(防内存泄漏)。 |
getSnapshot | 读取外部状态快照:返回当前状态值(如 localStorage 中对应 key 的值),React 通过对比快照判断是否重渲染。 |
getServerSnapshot | (SSR 可选)返回服务端初始快照,保证服务端 HTML 与客户端激活状态一致,否则 SSR 报错。 |
三、核心工作流程(4 步)
初始化订阅:组件首次渲染时,执行
subscribe注册外部状态监听(如监听storage事件)。读取初始快照:调用
getSnapshot拿到初始状态,作为组件首次渲染的值。状态变化通知:外部状态变化时,触发
subscribe中的 React 回调,告知 React “需检查状态”。对比快照更新:React 重新调用
getSnapshot拿新快照,对比新旧快照:
- 快照不同 → 触发组件重渲染,使用新值;
- 快照相同 → 跳过渲染,节省性能。
四、关键注意点
- 处理可变外部状态:若外部状态是引用类型(对象 / 数组),
getSnapshot需返回不可变快照(如[...arr]/{...obj}),否则 React 无法通过引用对比识别变化。 - 依赖返回值触发重渲染:
useSyncExternalStore的返回值是 React 唯一 “可追踪的状态”—— 不使用该返回值,即使外部状态变化,组件也不会重渲染(失去同步意义)。 - 当前标签页更新触发:如手动修改
localStorage,需主动派发storage事件(dispatchEvent(new StorageEvent('storage'))),否则当前标签页监听不到变化。
五、典型应用(封装 localStorage Hook)
核心是封装 “订阅 - 同步 - 更新” 逻辑,对外暴露类似 useState 的 [状态, 更新函数] 接口,示例核心代码:
const useStorage = (key, initValue) => {
// 1. 订阅 storage 事件
const subscribe = (callback) => {
window.addEventListener('storage', callback);
return () => window.removeEventListener('storage', callback);
};
// 2. 读取快照(处理序列化)
const getSnapshot = () => {
return JSON.parse(localStorage.getItem(key)) || initValue;
};
// 3. 同步状态(依赖 useSyncExternalStore 返回值)
const state = useSyncExternalStore(subscribe, getSnapshot);
// 4. 手动更新(主动派发事件触发同步)
const updateState = (value) => {
localStorage.setItem(key, JSON.stringify(value));
window.dispatchEvent(new StorageEvent('storage'));
};
return [state, updateState];
};