React19.2で導入されたActivityの紹介
開発環境
- react 19.2.0
- react-dom 19.2.0
前提
React 19.2 で新たに導入されたコンポーネント Activity は、DOMツリーから完全にアンマウントせずに「視覚的には非表示/再表示できる子コンポーネント領域」を作るためのラッパーです。
公式ドキュメントでは「UI と内部状態(state)を子要素に保持しつつ、表示・非表示を制御できる」ものと説明されています。
従来、条件付きレンダリング(例: isVisible && <Sidebar /> )を使うと、表示を切り替えるたびにコンポーネントがアンマウント/マウントされてしまい、内部 state や DOM の状態が失われてしまうケースがありました。
<Activity> を用いることで、アンマウントせずに「隠す」「表示する」が可能になります。
本題
Activityの基本仕様
(1)<Activity mode={visibility}> のように用い、mode プロパティには "visible" または "hidden" を指定します(デフォルトは "visible")
(2)mode="hidden" のとき
👉 子要素は display: none によって視覚的に非表示になります
👉 Effects(副作用)をクリーンアップ(アンマウント相当)し、不要な更新・サブスクリプションを避けられます
👉 ただし、子要素はアンマウントされず「状態(state)」「DOM構造」は保持されます
(3)mode="visible" のとき
👉 通常どおり表示と更新が行われます
主な活用シーン
(1)状態を保持したまま UI を切り替えたい場合 例:サイドバー、タブ、入力フォームなどで「一旦隠して、再表示したときに以前の入力値・展開状況を保持したい」場面。条件付き表示では state が破棄されるが、<Activity> では保持できます。
import { Activity, useState } from 'react';
function Sidebar() {
const [expanded, setExpanded] = useState(false);
return (
<div className="h-full w-56 border-r border-gray-200 bg-gray-100 p-4">
<button
className="rounded bg-blue-600 px-3 py-2 text-sm text-white hover:bg-blue-700"
onClick={() => setExpanded(!expanded)}
>
{expanded ? '閉じる' : '開く'}
</button>
{expanded && (
<ul className="mt-3 space-y-2 rounded border border-blue-100 bg-blue-50 p-3">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
)}
</div>
);
}
function App() {
const [showSidebar, setShowSidebar] = useState(true);
return (
<div className="flex h-screen">
{/* Activity でサイドバー表示/非表示 */}
<Activity mode={showSidebar ? 'visible' : 'hidden'}>
<Sidebar />
</Activity>
<main className="p-6">
<button
className="mb-4 rounded bg-gray-700 px-3 py-2 text-sm text-white hover:bg-gray-800"
onClick={() => setShowSidebar((v) => !v)}
>
サイドバーの{showSidebar ? '非表示' : '表示'}
</button>
<h1 className="text-2xl font-bold">Main Content</h1>
</main>
</div>
);
}
export default App;この例では、サイドバーを非表示にしても Sidebar の内部 expanded 状態は保持され、再表示時も前回の状態が復元されます。
(2)先読み・プリレンダリング用途
例:将来的に表示される可能性のあるコンポーネントを “hidden” モードで先にレンダリングしておき、ユーザーが切り替えた際に表示を高速化します。
import { Activity } from 'react';
function HeavyComponent() {
// 大量の初期処理/データ取得など
return <div>重たい画面</div>;
}
export default function App() {
const [showHeavy, setShowHeavy] = useState(false);
return (
<>
<button onClick={() => setShowHeavy(true)}>次の画面へ</button>
<Activity mode={showHeavy ? 'visible' : 'hidden'}>
<HeavyComponent />
</Activity>
</>
);
}ここでは HeavyComponent が非表示中でも低優先度でレンダリング準備されており、切り替え後の応答性が向上します。
注意事項
- Activity 内の子が「テキストだけ(DOM 要素なし)」の場合、DOM 要素を隠す対象がなく、期待どおりに動作しない可能性があります。公式ドキュメント上で注意喚起があります。
- <video>, <audio>, <iframe> など、明示的にクリーンアップが必要な副作用を含む要素を隠す場合は、クリーンアップを確実に実装する必要があります。
- 非表示中(mode="hidden")では、useEffect 内の処理が実行されない点を理解しておく必要があります。例えば非表示中にデータ取得を実行したい場合、Suspense 対応のフレームワークと組み合わせる必要があります。
さいごに
<Activity> は、React アプリケーションにおいて「表示・非表示を切り替えたいが、状態を捨てたくない」パターンをより簡潔に、かつ効率的に扱える新しい道具です。特にタブ切り替えやモーダル、サイドバーの表示制御などで有効です。 また、次画面の先読みなどパフォーマンス最適化の観点でも活用できます。ただし、動作の前提や副作用の扱いについては注意が必要です。これを機に、React 19.2 の Activity をぜひ導入検討してみてください。