React Hook useSyncExternalStore

一、核心定位

React 官方 Hook,用于安全订阅外部状态(如 localStorage、浏览器 API、全局变量、Rudex全局状态等),确保组件读取的外部状态始终一致,同时自动同步状态变化。

本质是通过 “订阅 - 发布模式” 实现与外部状态的同步,同时结合 React 内部的渲染调度机制,确保状态一致性。

二、核心参数(3 个,前 2 个必传)

参数名作用
subscribe注册外部状态监听:传入 React 内部回调(状态变时通知 React),返回 “取消订阅” 函数(防内存泄漏)。
getSnapshot读取外部状态快照:返回当前状态值(如 localStorage 中对应 key 的值),React 通过对比快照判断是否重渲染。
getServerSnapshot(SSR 可选)返回服务端初始快照,保证服务端 HTML 与客户端激活状态一致,否则 SSR 报错。

三、核心工作流程(4 步)

  1. 初始化订阅:组件首次渲染时,执行 subscribe 注册外部状态监听(如监听 storage 事件)。

  2. 读取初始快照:调用 getSnapshot 拿到初始状态,作为组件首次渲染的值。

  3. 状态变化通知:外部状态变化时,触发 subscribe 中的 React 回调,告知 React “需检查状态”。

  4. 对比快照更新:React 重新调用getSnapshot

    拿新快照,对比新旧快照:

    • 快照不同 → 触发组件重渲染,使用新值;
    • 快照相同 → 跳过渲染,节省性能。

四、关键注意点

  1. 处理可变外部状态:若外部状态是引用类型(对象 / 数组),getSnapshot 需返回不可变快照(如 [...arr]/{...obj}),否则 React 无法通过引用对比识别变化。
  2. 依赖返回值触发重渲染useSyncExternalStore 的返回值是 React 唯一 “可追踪的状态”—— 不使用该返回值,即使外部状态变化,组件也不会重渲染(失去同步意义)。
  3. 当前标签页更新触发:如手动修改 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];
};
← 返回列表