
这个 Hook 不是给业务代码用的
官方参考页第一段就是警告框:useInsertionEffect 是给 CSS-in-JS 库作者用的。如果你不是在写这类库、只是想插样式,你要的是 useEffect 或 useLayoutEffect。
所以这一篇的写法与前几篇不同:先说清它为什么存在,再说清什么时候不该碰它。
它解决的那个问题
CSS-in-JS 的运行时注入会这样干:组件渲染时生成一条规则,比如 .btn_abc { color: red },然后把 <style> 插进文档。
时机放错,两个方向都难受:
- 在渲染期间插,浏览器在并发更新下会逐帧重算样式,非常慢。
- 在
useEffect里插,其他 Effect 可能已经读过布局,读到的是旧样式算出来的结果。
useInsertionEffect 保证的是第二件事:等别的 Effect 跑起来时,<style> 已经就位。官方给它的定位是「在任何 layout Effect 触发之前,把元素插进 DOM」。
它不能假设 DOM 已经更新
这条最常被写错。官方原文是:useInsertionEffect 可能在 DOM 更新之前运行,也可能在之后,你不该依赖 DOM 在某个特定时刻已经更新。
同样的原因,它跑的时候 ref 还没挂上,所以读不到节点、读不到布局。另外两条限制:只在客户端运行,服务端渲染不执行;里面不能更新 state。
一句话概括它的能力边界:只做同步的样式插入,别的一概不做。
官方其实不建议运行时注入
这一点比 Hook 本身更值得记住。官方对 CSS-in-JS 的建议顺序是:
- 用编译器把静态样式抽出成 CSS 文件。
- 随状态变化的部分用内联样式。
运行时往 <style> 里插标签,官方明确不推荐:它会让浏览器频繁重算样式,时机不对时还会很慢。useInsertionEffect 只解决了后一半问题,前一半解决不了。
业务代码里该怎么做
不要在组件里手写 useInsertionEffect 去插样式。业务里有更省事的选择:CSS 文件、CSS Modules、原子化方案,动态的少数字段用内联样式。
真要动态插,useEffect 就够了。业务组件的样式变化不涉及「必须在布局计算之前完成」这条约束。
你会碰它的两种场合
一是在维护一个 CSS-in-JS 库,需要定注入时机;二是读这类库的源码,认出它在用这个 Hook,也知道它为什么不能用 useEffect 代替。
与另外两个 Effect 的分工
| Hook | 什么时候跑 | 拿来做什么 |
|---|---|---|
useInsertionEffect | 其他 layout Effect 之前 | 库注入样式 |
useLayoutEffect | DOM 变更之后、绘制之前 | 先量布局再改样式 |
useEffect | 绘制之后 | 同步外部系统:订阅、计时器、请求 |
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
换条路线
从其他文章中稳定抽取
最后更新于 ,距今已过 190 天
部分内容可能已过时