useInsertionEffect

陷阱

useInsertionEffect 适用于 CSS-in-JS 库的作者。除非您正在开发 CSS-in-JS 库并需要一个注入样式的地方,否则您可能更想要使用 useEffectuseLayoutEffect

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 将运行您的清理函数。

  • 可选 dependenciessetup 代码中引用的所有反应式值的列表。反应式值包括 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 有三种常见方法:

  1. 使用编译器将静态样式提取到 CSS 文件中
  2. 内联样式,例如 <div style={{ opacity: 1 }}>
  3. 运行时注入<style>标签

如果您使用 CSS-in-JS,我们建议结合前两种方法(CSS 文件用于静态样式,内联样式用于动态样式)。我们不推荐运行时注入<style>标签,原因如下:

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

深入探讨

这比在渲染期间或使用useLayoutEffect注入样式更好吗?

如果您在渲染期间插入样式,而 React 正在处理非阻塞更新,则浏览器会在渲染组件树时每一帧都重新计算样式,这可能非常慢。

useInsertionEffect比在useLayoutEffectuseEffect期间插入样式更好,因为它确保在组件中运行其他 Effects 时,<style>标签已插入。否则,由于样式已过期,常规 Effects 中的布局计算将出错。