useInsertionEffect
可以在布局副作用触发之前将元素插入到 DOM 中。
useInsertionEffect(setup, dependencies?)
参考
useInsertionEffect(setup, dependencies?)
调用 useInsertionEffect
在任何可能需要读取布局的副作用启动之前插入样式:
import { useInsertionEffect } from 'react';
// 在你的 CSS-in-JS 库中
function useCSS(rule) {
useInsertionEffect(() => {
// ... 在此注入 <style> 标签 ...
});
return rule;
}
参数
-
setup
:处理 Effect 的函数。setup 函数选择性返回一个 清理(cleanup) 函数。当你的组件添加到 DOM 中,但在任何布局触发之前,React 将运行你的 setup 函数。在每次重新渲染时,如果依赖项发生变化并且提供了 cleanup 函数,React 首先会使用旧值运行 cleanup 函数,然后使用新值运行你的 setup 函数。当你的组件从 DOM 中移除时,React 将运行你的 cleanup 函数。 -
可选
dependencies
:setup
代码中引用的所有响应式值的列表。响应式值包括 props、state 以及所有直接在组件内部声明的变量和函数。如果你的代码检查工具 配置了 React,那么它将验证是否每个响应式值都被正确地指定为依赖项。依赖列表必须具有固定数量的项,并且必须像[dep1, dep2, dep3]
这样内联编写。React 将使用Object.is
来比较每个依赖项和它先前的值。如果省略此参数,则将在每次重新渲染组件之后重新运行 Effect。
返回值
useInsertionEffect
返回 undefined
。
- Effect 只在客户端运行。在服务器渲染期间不会运行。
- 你不能在
useInsertionEffect
内部更新状态。 - 当
useInsertionEffect
运行时,refs
尚未附加。 useInsertionEffect
可能在 DOM 更新之前或之后运行。你不应该依赖于 DOM 在任何特定时间的更新状态。- 与其他类型的 Effect 不同,它们会先为每个 Effect 触发 cleanup 函数,然后再触发 setup 函数。而
useInsertionEffect
会同时触发 cleanup 函数和 setup 函数。这会导致 cleanup 函数和 setup 函数的“交错”执行。
用法
从 CSS-in-JS 库中注入动态样式
传统上,你会使用纯 CSS 为 React 组件设置样式。
// 在你的 JS 文件中:
<button className="success" />
// 在你的 CSS 文件中:
.success { color: green; }
有些团队更喜欢直接在 JavaScript 代码中编写样式,而不是编写 CSS 文件。这通常需要使用 CSS-in-JS 库或工具。以下是 CSS-in-JS 三种常见的实现方法:
- 使用编译器静态提取到 CSS 文件
- 内联样式,例如
<div style={{ opacity: 1 }}>
- 运行时注入
<style>
标签
如果你使用 CSS-in-JS,我们建议结合使用前两种方法(静态样式使用 CSS 文件,动态样式使用内联样式)。我们不建议运行时注入 <style>
标签有两个原因:
- 运行时注入会使浏览器频繁地重新计算样式。
- 如果在 React 生命周期中某个错误的时机进行运行时注入,它可能会非常慢。
第一个问题无法解决,但是 useInsertionEffect
可以帮助你解决第二个问题。
Call useInsertionEffect
to insert the styles before any layout Effects fire:
// 在你的 CSS-in-JS 库中
let isInserted = new Set();
function useCSS(rule) {
useInsertionEffect(() => {
// 同前所述,我们不建议在运行时注入 <style> 标签。
// 如果你必须这样做,那么应当在 useInsertionEffect 中进行。
if (!isInserted.has(rule)) {
isInserted.add(rule);
document.head.appendChild(getStyleForRule(rule));
}
});
return rule;
}
function Button() {
const className = useCSS('...');
return <div className={className} />;
}
与 useEffect
类似,useInsertionEffect
不在服务端运行。如果你需要收集在服务端上使用了哪些 CSS 规则,你可以在渲染期间进行:
let collectedRulesSet = new Set();
function useCSS(rule) {
if (typeof window === 'undefined') {
collectedRulesSet.add(rule);
}
useInsertionEffect(() => {
// ...
});
return rule;
}
阅读更多使用 useInsertionEffect
升级 CSS-in-JS 库的相关指南。
深入探讨
如果你在渲染期间注入样式并且 React 正在处理 非阻塞更新,那么浏览器将在渲染组件树时每一帧都会重新计算样式,这可能会 非常慢。
useInsertionEffect
比在 useLayoutEffect
或 useEffect
期间注入样式更好。因为它会确保 <style>
标签在其它 Effect 运行前被注入。否则,正常的 Effect 中的布局计算将由于过时的样式而出错。