optimize-conditional-rendering-activity
Activityコンポーネントを使用してすべての条件付きレンダリングを処理します。タブ、モーダル、表示/非表示UI、APIレスポンス状態(loading、error、empty、data)に必須です。
When & Why to Use This Skill
This Claude skill optimizes React application development by enforcing the use of the Activity component for all conditional rendering. It replaces standard JavaScript logical operators with a specialized visibility management pattern, ensuring that component states—such as form inputs, scroll positions, and internal variables—are preserved even when hidden. This approach significantly improves UI performance by reducing mount/unmount overhead and enables better pre-rendering and selective hydration.
Use Cases
- State-Preserving Tabs: Implementing tabbed interfaces where switching views must not reset user-entered data or scroll progress.
- Persistent Modals and Dialogs: Managing complex dialogs that need to retain their internal state and context while being toggled visible or hidden.
- API Response State Management: Handling transitions between loading, error, empty, and data states with a consistent and performant rendering pattern.
- Performance-Critical UI Toggles: Optimizing frequently toggled UI elements by avoiding the performance cost of re-mounting heavy component trees.
- Type-Safe Conditional Rendering: Utilizing optional chaining within Activity components to maintain type safety without the need for complex early-return guards.
| name | optimize-conditional-rendering-activity |
|---|---|
| description | Activityコンポーネントを使用してすべての条件付きレンダリングを処理します。タブ、モーダル、表示/非表示UI、APIレスポンス状態(loading、error、empty、data)に必須です。 |
Activity による条件付きレンダリングの最適化
すべての条件付きレンダリングは Activity コンポーネントを使用してください。
⚠️ 重要ルール
条件付きレンダリングは必ずActivityを使用してください。
禁止
{condition && <Component />}{condition ? <A /> : <B />}- CSS
display: noneによる表示/非表示
必須
<Activity mode={condition ? 'visible' : 'hidden'}><Component /></Activity>
例外
- early returnのみ(型narrowingが必要な場合)
// ✅ OK: early returnで型narrowing
function Component() {
const { data, error, isLoading } = useSWR(...)
if (isLoading) return <Spinner />
if (error) return <Alert>エラー</Alert>
if (!data) return <Empty />
// ここでdataはData型に確定
return <div>{data.name}</div>
}
// ❌ NG: 条件付きレンダリング
function Component() {
const { data } = useSWR(...)
return (
<div>
{data && <UserList data={data} />}
</div>
)
}
クイックスタート
パターン 1: タブUI
import { Activity } from 'react'
import { useState } from 'react'
export function TabPanel() {
const [activeTab, setActiveTab] = useState('profile')
return (
<div>
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList>
<TabsTrigger value="profile">プロフィール</TabsTrigger>
<TabsTrigger value="settings">設定</TabsTrigger>
</TabsList>
<div className="mt-4">
<Activity mode={activeTab === 'profile' ? 'visible' : 'hidden'}>
<ProfileTab />
</Activity>
<Activity mode={activeTab === 'settings' ? 'visible' : 'hidden'}>
<SettingsTab />
</Activity>
</div>
</Tabs>
</div>
)
}
パターン 2: モーダル/ダイアログ
import { Activity } from 'react'
export function UserDialog({ open, onOpenChange, userId }) {
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<Activity mode={open ? 'visible' : 'hidden'}>
<UserDetail userId={userId} />
</Activity>
</DialogContent>
</Dialog>
)
}
いつ使うか
必ず使用する場合:
- タブの構築(切り替え時にフォーム状態を保持)
- モーダルの構築(スクロール位置を保持)
- APIレスポンス状態の処理(loading、error、empty、data)
- 任意の条件付き表示/非表示UI
唯一の例外:
- 型narrowingが必要なearly returnパターン
重要ルール
reactからインポート:import { Activity } from 'react'- 表示には
<Activity mode="visible">を使用 - 非表示には
<Activity mode="hidden">を使用 - 非表示時もコンポーネント状態は保持される
- 条件付きレンダリングよりパフォーマンスが良い
- プリレンダリングと選択的ハイドレーションを可能にする
⚠️ Activity 内での型安全性
Activity は mode='hidden' でも DOM を保持するため、non-null assertion (!) は使用禁止です。
// ❌ NG: mode='hidden' 時に undefined で実行時エラー
<Activity mode={user ? 'visible' : 'hidden'}>
<div>{user!.email}</div>
<Badge>{getRoleLabel(user!.role)}</Badge>
</Activity>
// ✅ OK: optional chaining を使用
<Activity mode={user ? 'visible' : 'hidden'}>
<div>{user?.email}</div>
<Badge>{user?.role && getRoleLabel(user.role)}</Badge>
</Activity>
// ❌ OK: 子の中で && を使ってガード(コンポーネント全体を渡す場合。プリレンダリング対象が空のため、Activityの恩恵を得られない。stateも保持されない)
<Activity mode={user ? 'visible' : 'hidden'}>
{user && <UserProfile user={user} />}
</Activity>
Activity のネスト禁止
Activity コンポーネントはネストせず、二次的な条件は && 演算子を使用してください。
メリット
- 状態保持: コンポーネント状態(スクロール位置、フォーム入力、展開状態)が非表示時も保持される
- 型安全: ほとんどの場合で型narrowingが不要
- パフォーマンス: コンポーネントのマウント/アンマウントより高速
- 一貫性: すべての条件付きレンダリングが同じパターンを使用
- 可読性: 並列的な状態記述はネストした条件分岐より理解しやすい
よくある間違い
❌ && 演算子を使わない
// ❌ NG
{userName && <UserLink userName={userName} />}
{isLoading && <Spinner />}
❌ 三項演算子を表示/非表示に使わない
// ❌ NG
{isLoading ? <Spinner /> : <UserList />}
❌ CSS displayを使わない
// ❌ NG
<div style={{ display: isLoading ? 'block' : 'none' }}>
<Spinner />
</div>
✅ Activityを使う
// ✅ OK
<Activity mode={userName ? 'visible' : 'hidden'}>
<UserLink userName={userName} />
</Activity>
<Activity mode={isLoading ? 'visible' : 'hidden'}>
<Spinner />
</Activity>
関連スキル
- handle-edit-pages: 編集ページでloading/errorステート処理にActivityが必要
- handle-forms-rhf-zod: フォームの送信中/成功/エラー状態にActivityが必要
- manage-swr-data: データ取得・変更のloading/success/errorフィードバックにActivityが必要
- convert-enum-labels: Activity 内で enum ラベルを表示する際の optional chaining パターン
- format-dates: Activity 内で日付をフォーマットする際の optional chaining パターン