react - useSyncExternalStore 使用指南
文章目录一、 为什么需要 useSyncExternalStore1. 解决并发渲染中的“撕裂Tearing”现象2. 传统 useEffect 模式的痛点二、 API 语法解析三、 实战案例场景 1优雅地订阅浏览器 API如网络状态 navigator.onLine场景 2实现一个简易全局 Store手写简易版 Zustand四、 关键踩坑点死循环与引用一致性❌ 错误示范引起无限死循环✅ 正确示范保持不可变数据引用五、 总结与选型指南useSyncExternalStore是 React 18 引入的一个专门用于订阅外部数据源External Store的 Hook。虽然在日常业务组件开发中它的直接使用频率不如useState或useEffect高但它是各大状态管理库如 Redux, Zustand以及自定义 Browser API 订阅 Hook的底层核心基石。一、 为什么需要 useSyncExternalStore1. 解决并发渲染中的“撕裂Tearing”现象在 React 18 之前同步渲染模式渲染过程是一气呵成的不会被中途打断。而在 React 18 引入并发模式Concurrent React后渲染任务变成了可中断、可恢复的。如果在渲染过程中React 暂停了当前任务去处理更高优先级的用户交互而此时外部数据源非 React state 管理的数据如全局 store 或 window 属性发生了变更[组件 A 渲染] ── 读取外部值 1 │ ├─▶ (React 暂停渲染让出主线程) │ 外部数据源发生了更新值变成了 2 │ [组件 B 渲染] ── 读取外部值 2 最终 UI同一张页面上依赖相同数据的组件 A 和组件 B 展示了不一致的内容 这就是所谓的“UI 撕裂 (Tearing)”。2. 传统useEffect模式的痛点在 React 18 之前我们通常用useStateuseEffect订阅外部数据// ❌ 旧做法在并发模式下可能导致 UI 撕裂或闪烁 function useOnlineStatusOld() { const [isOnline, setIsOnline] useState(navigator.onLine); useEffect(() { const handleStatus () setIsOnline(navigator.onLine); window.addEventListener(online, handleStatus); window.addEventListener(offline, handleStatus); return () { window.removeEventListener(online, handleStatus); window.removeEventListener(offline, handleStatus); }; }, []); return isOnline; }缺陷 1UI 撕裂并发渲染期间无法保证状态读取的原子性。缺陷 2额外重绘/闪烁useEffect是在 DOM 挂载之后异步执行的组件首次渲染先拿到旧值挂载后再触发setState重新渲染容易导致 UI 闪烁。useSyncExternalStore的解决机制它强制以**同步Synchronous**方式读取外部数据快照。如果在渲染过程中外部 store 发生了变动React 会放弃当前的渲染立刻重新使用最新的快照再次发起渲染彻底消除了撕裂与闪烁。二、 API 语法解析const state useSyncExternalStore( subscribe, getSnapshot, getServerSnapshot? );参数类型作用subscribe(callback) unsubscribe注册订阅函数。当外部数据发生变化时调用传入的callback告诉 React“数据变了需要重新渲染”getSnapshot() State获取当前外部数据的快照函数。必须返回不可变数据或同一引用如果数据没变。getServerSnapshot?(可选)() State专为 SSR 服务端渲染提供的初始快照函数避免服务端与客户端 Hydration 不匹配。三、 实战案例场景 1优雅地订阅浏览器 API如网络状态navigator.onLine这是useSyncExternalStore在日常开发中最推荐的用例之一import { useSyncExternalStore } from react; // 1. 定义订阅逻辑 function subscribeOnline(callback: () void) { window.addEventListener(online, callback); window.addEventListener(offline, callback); return () { window.removeEventListener(online, callback); window.removeEventListener(offline, callback); }; } // 2. 定义快照获取函数 function getOnlineSnapshot() { return navigator.onLine; } // 3. 封装为自定义 Hook export function useOnlineStatus() { return useSyncExternalStore( subscribeOnline, getOnlineSnapshot, () true // SSR 场景下的默认兜底值 ); } // 4. 在组件中使用 function NetworkStatusBadge() { const isOnline useOnlineStatus(); return ( div className{isOnline ? bg-green : bg-red} {isOnline ? 网络正常 : ⚠️ 已断网} /div ); }场景 2实现一个简易全局 Store手写简易版 Zustand如果你想实现一个脱离 React 树管理可以在 React 组件外直接store.setState(...)的全局状态useSyncExternalStore是完美工具import { useSyncExternalStore } from react; // 1. 创建外部 Store function createStoreT(initialState: T) { let state initialState; const listeners new Set() void(); return { getState: () state, setState: (fnOrValue: T | ((prev: T) T)) { state typeof fnOrValue function ? (fnOrValue as (prev: T) T)(state) : fnOrValue; // 触发所有订阅者通知 React 更新 listeners.forEach(listener listener()); }, subscribe: (listener: () void) { listeners.add(listener); return () listeners.delete(listener); } }; } // 实例化 Store export const globalCountStore createStore({ count: 0 }); // 2. 在组件中使用 function CounterApp() { // 订阅 Store const state useSyncExternalStore( globalCountStore.subscribe, globalCountStore.getState ); return ( div h3Count: {state.count}/h3 button onClick{() globalCountStore.setState(s ({ count: s.count 1 }))} 加 1 (可从任意地方触发) /button /div ); }四、 关键踩坑点死循环与引用一致性在写getSnapshot时绝对不能每次都返回一个新创建的对象❌ 错误示范引起无限死循环function BadComponent() { const state useSyncExternalStore( store.subscribe, // ❌ 错误每次调用 getSnapshot 都返回一个新的对象引用 // React 会认为数据时刻在变化从而引发无限 Re-render 崩溃 () ({ count: store.getState().count }) ); }✅ 正确示范保持不可变数据引用// ✅ 方式 A只返回基本数据类型Primitive Value const count useSyncExternalStore( store.subscribe, () store.getState().count // 返回 number 基础类型天然支持 Object.is 比较 ); // ✅ 方式 B如果必须返回对象请确保 Store 内部更新时采用不可变Immutable深/浅拷贝 const state useSyncExternalStore( store.subscribe, store.getState // getState 保证在未更新时返回完全相同的对象引用 );五、 总结与选型指南维度useState/useReduceruseSyncExternalStore状态归属React 组件树内部React 状态React 外部如全局 Store, Window API, LocalStorage渲染模式异步并发/批处理渲染强制同步读取快照规避撕裂适用场景组件内部 UI 状态如弹窗开关、表单输入状态库开发、浏览器事件订阅、跨框架全局状态共享核心建议普通业务开发中处理组件自身的 State 依然优先选择useState只有在需要**订阅 React 框架外部的数据源如 window resize、media queries、自定义全局事件通知、封装 Redux/Zustand 这类外部 Store**时才使用useSyncExternalStore。 感谢阅读想了解更多 我的博客网站 | 记录思考分享干货 我的个人主页 | 关于我、开源项目

相关新闻

CSS 原子化

CSS 原子化

CSS 原子化 文章目录CSS 原子化一、代码直观对比:传统 CSS vs 原子化 CSS1. 传统方式(BEM 命名规范 独立 CSS)2. 原子化 CSS 方式(如 Tailwind CSS)二、CSS 范式演进对比三、原子化 CSS 的核心优势1. CSS 包体积的“边…

2026/10/25 11:19:47 阅读更多 →
Windows存储空间详解:原理、布局与实战配置指南

Windows存储空间详解:原理、布局与实战配置指南

1. 存储空间到底是什么?为什么你的Win10需要它 如果你用过Windows 10,大概率见过“存储空间”这个功能,它藏在控制面板的某个角落,看起来有点神秘,又有点高级。很多人可能直接把它和RAID(磁盘阵列&#xff…

2026/10/28 10:50:40 阅读更多 →
大模型部署实战:从Ollama到vLLM,三步实现Qwen2.5-7B本地运行

大模型部署实战:从Ollama到vLLM,三步实现Qwen2.5-7B本地运行

1. 从“玩具”到“工具”:为什么大模型部署是学习的第一道坎 最近和不少刚入行或者想转行做AI应用开发的朋友聊天,发现一个挺有意思的现象:大家一提到大模型,眼睛都放光,脑子里想的都是怎么用Prompt调教出一个无所不能…

2026/11/2 12:37:12 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/3 1:09:25 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/3 10:57:27 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/3 11:35:49 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/3 2:27:21 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/3 10:47:27 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/3 10:57:33 阅读更多 →

日新闻

周新闻

月新闻