useInsertionEffect 允许在任何布局效果触发之前将元素插入 DOM。
useInsertionEffect(setup, dependencies?)参考
🌐 Reference
useInsertionEffect(setup, dependencies?)
调用 useInsertionEffect 在可能需要读取布局的任何效果触发之前插入样式:
🌐 Call useInsertionEffect to insert styles before any Effects fire that may need to read layout:
import { useInsertionEffect } from 'react';
// Inside your CSS-in-JS library
function useCSS(rule) {
useInsertionEffect(() => {
// ... inject <style> tags here ...
});
return rule;
}参数
🌐 Parameters
setup:带有你 Effect 逻辑的函数。你的 setup 函数也可以选择返回一个 清理 函数。当你的组件被添加到 DOM 中,但在任何布局 Effect 执行之前,React 会运行你的 setup 函数。在每次具有变化依赖的重新渲染之后,React 会先运行清理函数(如果你提供了它)并使用旧的值,然后运行你的 setup 函数并使用新的值。当你的组件从 DOM 中移除时,React 会运行你的清理函数。- 可选
dependencies:在setup代码中引用的所有响应值的列表。响应值包括 props、state 以及直接在组件主体中声明的所有变量和函数。如果你的 linter 已 配置为 React,它将验证每个响应值是否正确指定为依赖。依赖列表必须具有固定数量的项目,并且需要像[dep1, dep2, dep3]一样内联书写。React 将使用Object.is比较算法,将每个依赖与其之前的值进行比较。如果你根本不指定依赖,则每次组件重新渲染后,你的 Effect 都会重新运行。
返回
🌐 Returns
useInsertionEffect 返回 undefined。
注意事项
🌐 Caveats
- 效果只在客户端运行。它们在服务器渲染期间不会运行。
- 你不能在
useInsertionEffect内部更新状态。 - 到
useInsertionEffect运行时,refs 还没有被附加。 useInsertionEffect可能会在 DOM 更新之前或之后运行。你不应该依赖 DOM 在任何特定时间被更新。- 与其他类型的 Effects 不同,其他类型的 Effects 会对每个 Effect 执行清理,然后对每个 Effect 执行设置,
useInsertionEffect会一次对一个组件同时执行清理和设置。这会导致清理函数和设置函数的“交错”执行。---
用法
🌐 Usage
从 CSS-in-JS 库注入动态样式
🌐 Injecting dynamic styles from CSS-in-JS libraries
传统上,你会使用纯 CSS 为 React 组件设置样式。
🌐 Traditionally, you would style React components using plain CSS.
// In your JS file:
<button className="success" />
// In your CSS file:
.success { color: green; }有些团队更喜欢直接在 JavaScript 代码中编写样式,而不是写 CSS 文件。这通常需要使用 CSS-in-JS 库或工具。CSS-in-JS 有三种常见的方法:
🌐 Some teams prefer to author styles directly in JavaScript code instead of writing CSS files. This usually requires using a CSS-in-JS library or a tool. There are three common approaches to CSS-in-JS:
- 使用编译器静态提取到 CSS 文件
- 内联样式,例如
<div style={{ opacity: 1 }}> <style>标签的运行时注入
如果你使用 CSS-in-JS,我们建议结合前两种方法(静态样式使用 CSS 文件,动态样式使用内联样式)。我们不推荐在运行时注入 <style> 标签,原因有两个:
🌐 If you use CSS-in-JS, we recommend a combination of the first two approaches (CSS files for static styles, inline styles for dynamic styles). We don’t recommend runtime <style> tag injection for two reasons:
- 运行时注入迫使浏览器更频繁地重新计算样式。
- 如果运行时注入发生在 React 生命周期的错误时间,它可能会非常慢。
第一个问题无法解决,但 useInsertionEffect 可以帮助你解决第二个问题。
🌐 The first problem is not solvable, but useInsertionEffect helps you solve the second problem.
调用 useInsertionEffect 在任何布局效果触发之前插入样式:
🌐 Call useInsertionEffect to insert the styles before any layout Effects fire:
// 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 规则,可以在渲染过程中进行:
🌐 Similarly to useEffect, useInsertionEffect does not run on the server. If you need to collect which CSS rules have been used on the server, you can do it during rendering:
let collectedRulesSet = new Set();
function useCSS(rule) {
if (typeof window === 'undefined') {
collectedRulesSet.add(rule);
}
useInsertionEffect(() => {
// ...
});
return rule;
}阅读更多关于将运行时注入的 CSS-in-JS 库升级到 useInsertionEffect 的内容。
深入研究
🌐 How is this better than injecting styles during rendering or useLayoutEffect?
如果你在渲染过程中插入样式,而 React 正在处理非阻塞更新, 浏览器将在渲染组件树的每一帧中重新计算样式,这可能会非常慢。
🌐 If you insert styles during rendering and React is processing a non-blocking update, the browser will recalculate the styles every single frame while rendering a component tree, which can be extremely slow.
useInsertionEffect 比在 useLayoutEffect 或 useEffect 期间插入样式更好,因为它可以确保在其他 Effects 在你的组件中运行之前,<style> 标签已经被插入。否则,由于样式过时,常规 Effects 中的布局计算将会错误。