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 函数还可以选择返回一个 *清理* 函数。当您的组件添加到 DOM 中但任何布局 Effects 触发之前,React 将运行您的 setup 函数。在每次具有更改依赖项的重新渲染之后,React 将首先使用旧值运行清理函数(如果提供了),然后使用新值运行您的 setup 函数。当您的组件从 DOM 中移除时,React 将运行您的清理函数。 -
可选
dependencies
:setup
代码中引用的所有反应式值的列表。反应式值包括 props、状态以及在组件主体中直接声明的所有变量和函数。如果您的代码检查器已针对 React 配置,它将验证每个反应式值是否都正确指定为依赖项。依赖项列表必须具有恒定的项数,并且必须像[dep1, dep2, dep3]
一样内联编写。React 将使用Object.is
比较算法将每个依赖项与其先前值进行比较。如果您根本没有指定依赖项,则您的 Effect 将在组件的每次重新渲染后重新运行。
返回值
useInsertionEffect
返回 undefined
。
警告
- Effects 仅在客户端运行。它们不会在服务器渲染期间运行。
- 您不能从
useInsertionEffect
内部更新状态。 - 当
useInsertionEffect
运行时,refs 尚未附加。 useInsertionEffect
可能在 DOM 更新之前或之后运行。你不应该依赖 DOM 在任何特定时间点进行更新。- 与其他类型的 Effects 不同,其他 Effects 会为每个 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
可在任何布局 Effects 触发之前插入样式。
// 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
期间插入样式更好,因为它确保在组件中运行其他 Effects 时,<style>
标签已插入。否则,由于样式已过期,常规 Effects 中的布局计算将出错。