useInsertionEffect

易犯错误

useInsertionEffect 是为 CSS-in-JS 库的作者准备的。除非你正在开发 CSS-in-JS 库并且需要一个注入样式的位置,否则你可能更想使用 useEffectuseLayoutEffect

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:

  1. 使用编译器静态提取到 CSS 文件
  2. 内联样式,例如 <div style={{ opacity: 1 }}>
  3. <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:

  1. 运行时注入迫使浏览器更频繁地重新计算样式。
  2. 如果运行时注入发生在 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 的内容。

深入研究

这比在渲染或 useLayout 副作用期间注入样式有何优势?

🌐 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 比在 useLayoutEffectuseEffect 期间插入样式更好,因为它可以确保在其他 Effects 在你的组件中运行之前,<style> 标签已经被插入。否则,由于样式过时,常规 Effects 中的布局计算将会错误。