useInsertionEffect

易犯错误

useInsertionEffect 适用于 CSS-in-JS 库作者。除非你正在使用 CSS-in-JS 库并且需要一个地方来注入样式,否则你可能需要 useEffectuseLayoutEffect

¥useInsertionEffect is for CSS-in-JS library authors. Unless you are working on a CSS-in-JS library and need a place to inject the styles, you probably want useEffect or useLayoutEffect instead.

useInsertionEffect 允许在任何布局效果触发之前将元素插入 DOM。

¥useInsertionEffect allows inserting elements into the DOM before any layout Effects fire.

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;
}

请参阅下面的更多示例。

¥See more examples below.

参数

¥Parameters

  • setup:具有副作用逻辑的函数。你的设置函数也可以选择返回一个清理函数。当你的组件添加到 DOM 时,但在触发任何布局效果之前,React 将运行你的设置函数。在每次使用更改的依赖重新渲染后,React 将首先使用旧值运行清理函数(如果你提供了它),然后使用新值运行你的设置函数。当你的组件从 DOM 中删除时,React 将运行你的清理函数。

    ¥setup: The function with your Effect’s logic. Your setup function may also optionally return a cleanup function. When your component is added to the DOM, but before any layout Effects fire, React will run your setup function. After every re-render with changed dependencies, React will first run the cleanup function (if you provided it) with the old values, and then run your setup function with the new values. When your component is removed from the DOM, React will run your cleanup function.

  • 可选 dependenciessetup 代码中引用的所有反应值的列表。反应值包括属性、状态以及直接在组件主体内声明的所有变量和函数。如果你的 linter 是 为 React 配置,它将验证每个反应值是否正确指定为依赖。依赖列表必须具有恒定数量的条目,并且像 [dep1, dep2, dep3] 一样写成内联。React 将使用 Object.is 比较算法将每个依赖与其先前的值进行比较。如果你根本不指定依赖,你的副作用将在每次重新渲染组件后重新运行。

    ¥optional dependencies: The list of all reactive values referenced inside of the setup code. Reactive values include props, state, and all the variables and functions declared directly inside your component body. If your linter is configured for React, it will verify that every reactive value is correctly specified as a dependency. The list of dependencies must have a constant number of items and be written inline like [dep1, dep2, dep3]. React will compare each dependency with its previous value using the Object.is comparison algorithm. If you don’t specify the dependencies at all, your Effect will re-run after every re-render of the component.

返回

¥Returns

useInsertionEffect 返回 undefined

¥useInsertionEffect returns undefined.

注意事项

¥Caveats

  • 副作用仅在客户端上运行。它们不会在服务器渲染期间运行。

    ¥Effects only run on the client. They don’t run during server rendering.

  • 你无法从 useInsertionEffect 内部更新状态。

    ¥You can’t update state from inside useInsertionEffect.

  • useInsertionEffect 运行时,裁判尚未附加。

    ¥By the time useInsertionEffect runs, refs are not attached yet.

  • useInsertionEffect 可以在 DOM 更新之前或之后运行。你不应该依赖于在任何特定时间更新 DOM。

    ¥useInsertionEffect may run either before or after the DOM has been updated. You shouldn’t rely on the DOM being updated at any particular time.

  • 与其他类型的效果不同的是,其他类型的效果会为每个效果触发清理,然后为每个效果进行设置,而 useInsertionEffect 将一次触发一个组件的清理和设置。这导致了 “interleaving” 的清理和设置功能。

    ¥Unlike other types of Effects, which fire cleanup for every Effect and then setup for every Effect, useInsertionEffect will fire both cleanup and setup one component at a time. This results in an “interleaving” of the cleanup and setup functions.


用法

¥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 文件

    ¥Static extraction to CSS files with a compiler

  2. 内联样式,例如 <div style={{ opacity: 1 }}>

    ¥Inline styles, e.g. <div style={{ opacity: 1 }}>

  3. <style> 标签的运行时注入

    ¥Runtime injection of <style> tags

如果你使用 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. 运行时注入迫使浏览器更频繁地重新计算样式。

    ¥Runtime injection forces the browser to recalculate the styles a lot more often.

  2. 如果运行时注入发生在 React 生命周期的错误时间,它可能会非常慢。

    ¥Runtime injection can be very slow if it happens at the wrong time in the React lifecycle.

第一个问题无解,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 的信息。

¥Read more about upgrading CSS-in-JS libraries with runtime injection to 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 期间插入样式更好,因为它确保当其他副作用在你的组件中运行时,<style> 标签已经插入。否则,常规副作用中的布局计算将由于过时的样式而出错。

¥useInsertionEffect is better than inserting styles during useLayoutEffect or useEffect because it ensures that by the time other Effects run in your components, the <style> tags have already been inserted. Otherwise, layout calculations in regular Effects would be wrong due to outdated styles.


React 中文网 - 粤ICP备13048890号