useInsertionEffect
允许在任何布局 Effects 触发之前将元素插入 DOM。
useInsertionEffect(setup, dependencies?)
参考
useInsertionEffect(setup, dependencies?)
调用 useInsertionEffect
在任何可能需要读取布局的 Effects 触发之前插入样式
import { useInsertionEffect } from 'react';
// Inside your CSS-in-JS library
function useCSS(rule) {
useInsertionEffect(() => {
// ... inject <style> tags here ...
});
return rule;
}
参数
-
setup
:包含 Effect 逻辑的函数。您的 setup 函数还可以选择返回一个 *cleanup* 函数。当您的组件被添加到 DOM 时,但在任何布局 Effects 触发之前,React 将运行您的 setup 函数。每次重新渲染并更改依赖项后,React 将首先使用旧值运行 cleanup 函数(如果您提供了它),然后使用新值运行您的 setup 函数。当您的组件从 DOM 中移除时,React 将运行您的 cleanup 函数。 -
可选
dependencies
:setup
代码中引用的所有响应式值的列表。响应式值包括 props、state 以及直接在组件主体中声明的所有变量和函数。如果您的 linter 配置为 React,它将验证每个响应式值是否都正确指定为依赖项。依赖项列表必须具有恒定的项数,并且像[dep1, dep2, dep3]
一样内联编写。React 将使用Object.is
比较算法将每个依赖项与其先前的值进行比较。如果您根本没有指定依赖项,您的 Effect 将在组件的每次重新渲染后重新运行。
返回值
useInsertionEffect
返回 undefined
。
注意事项
- Effects 仅在客户端运行。它们在服务器渲染期间不运行。
- 您不能从
useInsertionEffect
内部更新状态。 - 在
useInsertionEffect
运行时, useInsertionEffect
的运行时间可能在 DOM 更新之前,也可能在 DOM 更新之后。你不应该依赖于 DOM 在任何特定时间点被更新。- 与其他类型的 Effect 不同,其他类型的 Effect 会针对每个 Effect 触发清理函数,然后针对每个 Effect 触发设置函数,而
useInsertionEffect
会一次性针对一个组件触发清理和设置函数。这会导致清理函数和设置函数“交错”执行。
用法
从 CSS-in-JS 库注入动态样式
传统上,你会使用普通的 CSS 来设置 React 组件的样式。
// In your JS file:
<button className="success" />
// In your CSS file:
.success { color: green; }
有些团队更喜欢直接在 JavaScript 代码中编写样式,而不是编写 CSS 文件。这通常需要使用 CSS-in-JS 库或工具。CSS-in-JS 有三种常见的方法
- 使用编译器静态提取到 CSS 文件
- 内联样式,例如
<div style={{ opacity: 1 }}>
- 运行时注入
<style>
标签
如果你使用 CSS-in-JS,我们建议结合使用前两种方法(CSS 文件用于静态样式,内联样式用于动态样式)。我们不建议运行时注入 <style>
标签,原因有两个:
- 运行时注入会导致浏览器更频繁地重新计算样式。
- 如果在 React 生命周期中的错误时间发生,运行时注入可能会非常慢。
第一个问题无法解决,但 useInsertionEffect
可以帮助你解决第二个问题。
调用 useInsertionEffect
在任何布局 Effect 触发之前插入样式
// Inside your CSS-in-JS library
let isInserted = new Set();
function useCSS(rule) {
useInsertionEffect(() => {
// As explained earlier, we don't recommend runtime injection of <style> tags.
// But if you have to do it, then it's important to do in 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
中插入样式更好,因为它可以确保在组件中其他 Effect 运行时,<style>
标签已经被插入。否则,由于样式过时,常规 Effect 中的布局计算将是错误的。