useInsertionEffect

useInsertionEffect が CSS-in-JS ライブラリ実装向けである理由を学ぶ。

useInsertionEffect は、レイアウトを読む Effect より前に動的なスタイルを挿入するための特殊な Effect Hook です。主に CSS-in-JS ライブラリの作者が使う API で、通常のアプリケーションコードではほとんど必要ありません。

スタイルを先に登録する

動的な CSS ルールを <style> 要素へ登録するライブラリでは、レイアウト計測より前にルールを入れる必要があります。

import { useInsertionEffect } from 'react';

function useStyleRule(rule: string) {
  useInsertionEffect(() => {
    const style = document.createElement('style');
    style.textContent = rule;
    document.head.append(style);
    return () => style.remove();
  }, [rule]);
}

この例は仕組みを示す最小例です。一般の UI コンポーネントで document.head を直接操作する実装として使うものではありません。すでにスタイルライブラリを利用しているなら、そのライブラリの API で className やテーマを指定します。

これにより useLayoutEffect で計測する時点にはスタイルが適用されています。

実際の CSS-in-JS ライブラリは、同じルールを何度も挿入しないキャッシュ、SSR で集めたスタイルとの整合、コンポーネント削除時の参照数管理も必要です。この Hook はそれらの実装で「挿入するタイミング」だけを支援します。

制約を理解する

この Hook 内では state を更新できず、ref の内容も期待どおり利用できません。DOM を読んだり、描画結果を測定したりする場所でもありません。アプリでクラス名や style 属性を付けるだけなら JSX を使い、外部システムとの同期には useEffect を使います。

useInsertionEffect は DOM 更新の前にも後にも実行され得ます。「DOM がまだ変更されていない」ことを前提にコードを書いてはいけません。保証されるのは、レイアウトを読む Effect より先にスタイルを挿入できることです。

useInsertionEffect を useLayoutEffect より早く実行したいという理由だけでアプリに導入すると、React の更新モデルへの依存が強くなります。コンポーネント利用者としては、採用中のスタイルライブラリが内部で使うことを理解していれば十分です。

useEffect / useLayoutEffect との整理

外部システムへ接続するなら useEffect、DOM を測って描画前に補正するなら useLayoutEffect、動的 CSS を挿入するライブラリを作るなら useInsertionEffect です。名前の順序で選ぶのではなく、処理の目的で選びます。通常のアプリで最後の Hook を選ぶ状況はほぼありません。

まとめ

useInsertionEffect はスタイルエンジンのための低レベル API です。CSS-in-JS を利用する側が直接呼ぶものではなく、通常はライブラリに任せます。

Sources