【翻译】了解 React18 的新Hook - useInsertionEffect

366 阅读2分钟

原文地址:Know about the useInsertionEffect hook in React 18 | Saeloun Blog

React18 为 Concurrent mode 提供了基石,同时增加了几个能够让用户充分利用 Concurrent mode 特性的新hooks。在本篇博客中,我们就来展开讲讲全新引入的useInsertionEffect

CSS In JS 库所遇到的问题

这些库会在运行时,生成新的 CSS 规则样式,然后把 Style 标签插到 Document 中。由于可能会影响到程序性能,对于这些库来说,我们有必要知道插入 Style 标签的时机是什么。

当我们增加或者删除某些 CSS 规则的时候,浏览器或多或少都会重新计算所有 CSS 规则。浏览器不仅仅是将这些 CSS 规则应用到仅变化的那些 DOM 节点,而是会将所有 CSS 规则应用到所有的 DOM 节点上。

换句话说,这种操作会导致,在 React 正在渲染期间,每一帧绘制的时候,对所有 DOM 上的所有 CSS 规则都会重算。这就导致应用很慢。

浏览器内置的优化手段并解决不了这种问题,

有一种可以解决这种问题的方法就是管理好更新 DOM 的时机。

我们必须确保当对某个 DOM 节点修改的同时,来修改 CSS 样式规则。换句话说,这个时机点可能是获取布局信息之前+内容绘制在浏览器之前和当 React 修改 DOM 节点的时候。

这个行为跟useLayoutEffect这个 Hook 很相似。尽管这个 Hook 提供了我们所需要的行为,但是我们不能使用这个 Hook 来插入样式标签

为什么不能使用useLayoutEffect

useLayoutEffect是用来获取 DOM 的布局信息和同步阻塞 React 重渲染。

打个比方,假设我们有些组件往页面里插入 Style 标签,而有些组件需要去获取布局信息。如果我们使用useLayoutEffect来去插入 Style 样式,这会导致整个布局一次性多次计算。而且如果有个 Hook 在 CSS 被插入到 Document 前获取布局信息,会导致读取的布局信息是错误的。

所以,为了解决这个问题,React18 增加了useInsertionEffect这个 Hook

useInsertionEffect的用法跟useEffect差不多,但是useInsertionEffect执行时机是在所有dom节点开始改动前,而且是同步触发的

这个Hook应该是用来在useLayoutEffect的 Hook 获取布局信息之前,插入全局的 DOM(比如 Style 标签或者 SVG 的 Defs 标签)

这个Hook也有限制,因为他不能获取到refs,而且不能触发 React 更新

//Source code - https://github.com/reactwg/react-18/discussions/110

function useCSS(rule) {
  useInsertionEffect(() => {
    if (!isInserted.has(rule)) {
      isInserted.add(rule);
      document.head.appendChild(getStyleForRule(rule));
    }
  });
  return rule;
}
function App() {
  let className = useCSS(rule);
  return <div className={className} />;
}

注意useInsertionEffect在 SSR 阶段是不会执行的

想更深入了解这个hook可以看这个: the PR and more discussion here.