props を受け取らない jotai atom 駆動コンポーネントは useHydrateAtoms でラッパー経由に初期状態を注入する
React
テスト
jotai
知識
判断
モーダルの開閉のような UI 状態を props ではなくグローバルな jotai atom(例: atom<boolean>(false))だけで制御しているコンポーネントは、Storybook のストーリーやコンポーネント単体プレビューのように「このコンポーネントを特定の状態から描画したい」場面で、args や props から直接状態を渡す手段がない。
jotai/utils の useHydrateAtoms を使うと、対象コンポーネントをラップする小さなコンポーネントを1つ用意し、その中で useHydrateAtoms([[targetAtom, true]]) を呼んでから対象コンポーネントを render すれば、コンポーネント本体を一切変更せずに初期状態を注入できる。
採用理由(直接 store.set() との比較)
useHydrateAtomsは「まだ atom が読まれていない場合にだけ初期値を適用する」設計で、React のレンダーサイクル内で完結する。テストコード側から jotai の store インスタンスを取り回してstore.set(atom, value)を render 前に呼ぶ方式と比べ、store の取得・受け渡しを別途配線しなくてよい。- hydrate は冪等(既に値が設定済み・ユーザー操作で変化済みの atom には効かない)なので、再レンダー時に意図せず値を巻き戻さない。
落とし穴
- テストランナー側で atom の状態がケース間にリークしないよう、jotai の
Providerにstore={createStore()}で毎回新しい store を渡す運用と組み合わせる。store を使い回すと、あるケースで開いた atom が別ケースの初期描画にも残ってしまう。 - 複数の Provider / store を使い分けている場合、
useHydrateAtomsはデフォルトで直近の Provider の store を対象にするため、対象コンポーネントと同じ store 配下でラッパーを render する必要がある。
適用条件
- 対象コンポーネントが状態を props として公開しておらず、かつコンポーネント本体を編集できない(他プロジェクトの資産、既存コンポーネントの変更禁止方針など)場合に有効。コンポーネントを自由に編集できるなら、素直に props 経由で状態を渡せるようにする方が正攻法。
検証
ラッパー経由で描画した際に、対象コンポーネントが期待する開いた状態のマークアップ(モーダルなら dialog role 等)が初期描画時点で存在することを確認する。