React 用 useSyncExternalStore 接入外部可变数据源:浏览器 API、第三方 store 与订阅
test12026-10-030 次阅读
为什么需要 useSyncExternalStore
React 18 引入的 useSyncExternalStore 是读取“存在于 React 之外的可变数据”的官方方案。过去我们常用 useState 加 useEffect 去订阅浏览器 API(如 online 状态、localStorage、媒体查询)或第三方状态库,但这类写法在并发渲染下容易出现撕裂(tearing)——同一个渲染结果里读到了新旧两个状态。
useSyncExternalStore 把“订阅”和“读取快照”拆成两个纯函数,React 能在渲染前强制拿到一致的快照,从根本上规避撕裂。
最小可用签名
它接收三个参数:subscribe(订阅函数)、getSnapshot(返回当前快照)、以及可选的 getServerSnapshot(SSR 时使用)。
import { useSyncExternalStore } from "react";
function useOnlineStatus() {
const isOnline = useSyncExternalStore(
(callback) => {
window.addEventListener("online", callback);
window.addEventListener("offline", callback);
return () => {
window.removeEventListener("online", callback);
window.removeEventListener("offline", callback);
};
},
() => navigator.onLine,
() => true
);
return isOnline;
}
封装一个可复用的 localStorage store
把任意 localStorage 键封装成响应式数据源,订阅 window 的 storage 事件即可在多标签页间同步。
function createStorageStore(key: string) {
const listeners = new Set<() => void>();
window.addEventListener("storage", (e) => {
if (e.key === key) listeners.forEach((l) => l());
});
return {
subscribe(listener: () => void) {
listeners.add(listener);
return () => listeners.delete(listener);
},
getSnapshot() {
return window.localStorage.getItem(key);
},
};
}
const themeStore = createStorageStore("theme");
function useTheme() {
return useSyncExternalStore(
themeStore.subscribe,
themeStore.getSnapshot,
() => "light"
);
}
接入第三方 store 的注意事项
- getSnapshot 必须返回缓存值:如果每次都返回新对象(如展开运算符造对象),React 会陷入无限重渲染,应在 store 内部保留上一次引用的缓存。
- 订阅函数必须返回取消订阅函数,否则组件卸载时会泄漏监听器。
- 快照要可比较:基本类型、字符串、或稳定引用的对象最安全;返回数组或对象时记得做引用缓存。
掌握这条规则后,任何“React 之外的真相来源”都能干净地接进组件树,而不必再为了规避撕裂去堆 useEffect 和 useState。
T
test1
文章作者
为什么需要 useSyncExternalStore React 18 引入的 useSyncExternalStor...
- 分类
- 技术
- 发布时间
- 2026-10-03
- 字数
- 约 1791 字
- 阅读
- 0 次
