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 函式也可以選擇性地返回一個*cleanup*函式。當您的元件被添加到 DOM 時,但在任何佈局 Effects 觸發之前,React 將運行您的 setup 函式。在每次重新渲染並更改 dependencies 後,React 將首先使用舊值運行 cleanup 函式(如果您提供的話),然後使用新值運行您的 setup 函式。當您的元件從 DOM 中移除時,React 將運行您的 cleanup 函式。

  • 選用 dependencies:在 setup 程式碼內參考的所有反應值的列表。反應值包括 props、state,以及所有直接在元件主體內宣告的變數和函式。如果您的 linter 已針對 React 進行設定,它將驗證每個反應值是否已正確指定為 dependency。dependency 列表必須具有固定數量的項目,並且像 [dep1, dep2, dep3] 一樣內嵌撰寫。React 將使用 Object.is 比較演算法將每個 dependency 與其先前的值進行比較。如果您完全未指定 dependencies,則您的 Effect 將在每次元件重新渲染後重新執行。

回傳值

useInsertionEffect 返回 undefined

注意事項

  • Effects 僅在客戶端上運行。它們在伺服器渲染期間不運行。
  • 您無法在 useInsertionEffect 內部更新狀態。
  • useInsertionEffect 執行時,refs 還未附加。
  • useInsertionEffect 可能在 DOM 更新之前或之後執行。您不應該依賴 DOM 在任何特定時間點已更新。
  • 不同於其他類型的 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;
}

閱讀更多關於使用執行階段注入將 CSS-in-JS 函式庫升級到 useInsertionEffect 的資訊。

深入探討

這比在渲染期間或 useLayoutEffect 注入樣式好在哪裡?

如果您在渲染期間插入樣式,而 React 正在處理 非阻塞更新,瀏覽器會在渲染組件樹時,每影格重新計算樣式,這可能會非常慢。

useInsertionEffect 比在 useLayoutEffectuseEffect 期間插入樣式更好,因為它可以確保在您的組件中執行其他 Effects 時,<style> 標籤已經插入。否則,由於樣式過時,一般 Effects 中的佈局計算會錯誤。