|
|
@@ -0,0 +1,256 @@
|
|
|
+# Jotai 移行 TODO リスト
|
|
|
+
|
|
|
+## 現在の進捗状況
|
|
|
+
|
|
|
+### ✅ 移行完了済み
|
|
|
+
|
|
|
+1. **サイドバー関連(完了済み)**
|
|
|
+ - `useDrawerOpened`: サイドバーのドロワー表示状態
|
|
|
+ - `usePreferCollapsedMode`: サイドバーの折りたたみモード設定(永続化含む)
|
|
|
+ - `useSidebarMode`: サイドバーの表示モード管理(`DRAWER`, `COLLAPSED`, `DOCK`)
|
|
|
+
|
|
|
+2. **デバイス状態(完了済み)**
|
|
|
+ - `useDeviceLargerThanXl`: デバイスサイズ判定
|
|
|
+
|
|
|
+3. **エディター状態(部分的)**
|
|
|
+ - `useEditorMode`: エディターモード管理(TODO コメント有り)
|
|
|
+
|
|
|
+## 🚧 次に移行すべき状態(優先度順)
|
|
|
+
|
|
|
+### ✅ SSR対応の改善(完了)
|
|
|
+
|
|
|
+**実施内容:**
|
|
|
+- ~~`usePreferCollapsedModeInitializer` を削除し、`useHydrateAtoms` ベースの `UIStateHydrator` コンポーネントに移行~~
|
|
|
+- ✅ `usePreferCollapsedModeInitializer` を削除し、`useHydrateAtoms` ベースの `useHydrateSidebarAtoms` フックに移行
|
|
|
+- HoC パターンからカスタムフックパターンに変更し、よりシンプルで自然な実装に
|
|
|
+- 各ページで `useHydrateSidebarAtoms` を使用してJotai atomsの初期化を統一的に実行
|
|
|
+- SSR/CSRハイドレーション不整合の問題を解決
|
|
|
+
|
|
|
+**変更ファイル:**
|
|
|
+- ~~`states/ui/UIStateHydrator.tsx`: 新規作成~~ → `states/hydrate/sidebar.ts`: 新規作成(改良)
|
|
|
+- `states/ui/sidebar.ts`: initializer フック削除、base atom のエクスポート追加
|
|
|
+- `pages/utils/commons.ts`: `useInitSidebarConfig` 関数を簡素化
|
|
|
+- `pages/[[...path]].page.tsx`: `useHydrateSidebarAtoms` フック使用
|
|
|
+- `pages/_private-legacy-pages.page.tsx`: `useHydrateSidebarAtoms` フック使用
|
|
|
+
|
|
|
+**メリット:**
|
|
|
+- ✅ Jotai公式のSSR対応パターンに準拠
|
|
|
+- ✅ 複数atomsの統一的な初期化
|
|
|
+- ✅ ハイドレーション不整合の解決
|
|
|
+- ✅ 将来の状態追加時の拡張性向上
|
|
|
+- ✅ HoCよりもシンプルなフックパターン
|
|
|
+- ✅ JSX階層がフラット
|
|
|
+- ✅ より自然な React Hook の使用感
|
|
|
+
|
|
|
+### 優先度 1: UI 状態(クライアントサイド完結)
|
|
|
+
|
|
|
+#### 1.1 サイドバー関連の残り
|
|
|
+- [ ] `useCurrentSidebarContents`: サイドバーのコンテンツタイプ
|
|
|
+ - **ファイル**: `/workspace/growi/apps/app/src/stores/ui.tsx:156`
|
|
|
+ - **使用箇所**:
|
|
|
+ - `SidebarContents.tsx`
|
|
|
+ - `PrimaryItem.tsx`
|
|
|
+ - `pages/utils/commons.ts`
|
|
|
+ - **特徴**: 永続化必要(`scheduleToPut({ currentSidebarContents: data })`)
|
|
|
+
|
|
|
+- [ ] `useCollapsedContentsOpened`: 折りたたまれたコンテンツの開閉状態
|
|
|
+ - **ファイル**: `/workspace/growi/apps/app/src/stores/ui.tsx:188`
|
|
|
+ - **使用箇所**:
|
|
|
+ - `ToggleCollapseButton.tsx`
|
|
|
+ - `PrimaryItem.tsx`
|
|
|
+ - `SidebarContents.tsx`
|
|
|
+ - `Sidebar.tsx`
|
|
|
+ - `UISettings.tsx`
|
|
|
+ - **特徴**: 一時的な状態(永続化不要)
|
|
|
+
|
|
|
+- [ ] `useCurrentProductNavWidth`: プロダクトナビゲーションの幅
|
|
|
+ - **ファイル**: `/workspace/growi/apps/app/src/stores/ui.tsx:175`
|
|
|
+ - **使用箇所**:
|
|
|
+ - `PagePathNavSticky.tsx`
|
|
|
+ - `Sidebar.tsx`
|
|
|
+ - `pages/utils/commons.ts`
|
|
|
+ - **特徴**: 永続化必要(`scheduleToPut({ currentProductNavWidth: data })`)
|
|
|
+
|
|
|
+#### 1.2 ページ関連の UI 状態
|
|
|
+- [ ] `usePageControlsX`: ページコントロールのX座標
|
|
|
+ - **ファイル**: `/workspace/growi/apps/app/src/stores/ui.tsx:171`
|
|
|
+ - **使用箇所**:
|
|
|
+ - `PagePathNavSticky.tsx`
|
|
|
+ - `PageControls.tsx`
|
|
|
+ - `PageHeader.tsx`
|
|
|
+ - **特徴**: 一時的な状態(永続化不要)
|
|
|
+
|
|
|
+- [ ] `useSelectedGrant`: 選択された権限設定
|
|
|
+ - **ファイル**: `/workspace/growi/apps/app/src/stores/ui.tsx:192`
|
|
|
+ - **使用箇所**:
|
|
|
+ - `SavePageControls.tsx`
|
|
|
+ - `GrantSelector.tsx`
|
|
|
+ - `PageEditor.tsx`
|
|
|
+ - **特徴**: エディター内の一時的な状態
|
|
|
+
|
|
|
+### 優先度 2: モーダル状態
|
|
|
+
|
|
|
+#### 2.1 各種モーダルの開閉状態
|
|
|
+すべて `/workspace/growi/apps/app/src/stores/modal.tsx` に実装されている:
|
|
|
+
|
|
|
+- [ ] `usePageCreateModal`: ページ作成モーダル
|
|
|
+- [ ] `useGrantedGroupsInheritanceSelectModal`: グループ継承選択モーダル
|
|
|
+- [ ] `useDeleteModal`: 削除モーダル
|
|
|
+- [ ] `useEmptyTrashModal`: ゴミ箱空モーダル
|
|
|
+- [ ] `useDuplicateModal`: 複製モーダル
|
|
|
+- [ ] `useRenameModal`: リネームモーダル
|
|
|
+- [ ] `usePutBackPageModal`: ページ復元モーダル
|
|
|
+- [ ] `usePresentationModal`: プレゼンテーションモーダル
|
|
|
+- [ ] `usePrivateLegacyPagesMigrationModal`: プライベートページ移行モーダル
|
|
|
+
|
|
|
+**特徴**: すべて一時的な状態で永続化不要
|
|
|
+
|
|
|
+### 優先度 3: データ関連状態(SWR 継続使用を検討)
|
|
|
+
|
|
|
+以下は SWR での管理を継続すべきか検討が必要:
|
|
|
+
|
|
|
+- [ ] **Alert 系** (`/workspace/growi/apps/app/src/stores/alert.tsx`)
|
|
|
+ - `usePageStatusAlert`: ページステータスアラート
|
|
|
+
|
|
|
+- [ ] **YJS 関連** (`/workspace/growi/apps/app/src/stores/yjs.ts`)
|
|
|
+ - `useCurrentPageYjsData`: YJS データ
|
|
|
+
|
|
|
+- [ ] **リモートページ関連** (`/workspace/growi/apps/app/src/stores/remote-latest-page.ts`)
|
|
|
+ - `useRemoteRevisionId`
|
|
|
+ - `useRemoteRevisionBody`
|
|
|
+ - `useRemoteRevisionLastUpdateUser`
|
|
|
+ - `useRemoteRevisionLastUpdatedAt`
|
|
|
+
|
|
|
+- [ ] **編集クライアント** (`/workspace/growi/apps/app/src/stores/use-editing-clients.ts`)
|
|
|
+ - `useEditingClients`
|
|
|
+
|
|
|
+## 🎯 次の実装ステップ
|
|
|
+
|
|
|
+### ステップ 1: 優先度1の UI 状態を移行
|
|
|
+
|
|
|
+1. **`useCurrentSidebarContents` の移行**
|
|
|
+ - 新しい atom を `states/ui/sidebar.ts` に追加
|
|
|
+ - 永続化機能を実装(`usePreferCollapsedMode` のパターンを参考)
|
|
|
+ - 使用箇所を順次更新
|
|
|
+
|
|
|
+2. **`useCollapsedContentsOpened` の移行**
|
|
|
+ - シンプルな boolean atom として実装
|
|
|
+ - 使用箇所を順次更新
|
|
|
+
|
|
|
+3. **`useCurrentProductNavWidth` の移行**
|
|
|
+ - 永続化機能付きの atom として実装
|
|
|
+ - 使用箇所を順次更新
|
|
|
+
|
|
|
+4. **`usePageControlsX` の移行**
|
|
|
+ - シンプルな number atom として実装
|
|
|
+ - 使用箇所を順次更新
|
|
|
+
|
|
|
+5. **`useSelectedGrant` の移行**
|
|
|
+ - エディター関連の atom として `states/ui/editor.ts` に追加
|
|
|
+ - 使用箇所を順次更新
|
|
|
+
|
|
|
+### ステップ 2: モーダル状態の移行
|
|
|
+
|
|
|
+1. **新しいファイル作成**: `states/ui/modal.ts`
|
|
|
+2. **各モーダルの atom を実装**
|
|
|
+ - 統一的なパターンでモーダル状態を管理
|
|
|
+ - 開閉とデータ保持の機能を提供
|
|
|
+3. **既存の `stores/modal.tsx` から順次移行**
|
|
|
+
|
|
|
+### ステップ 3: 移行完了後のクリーンアップ
|
|
|
+
|
|
|
+1. **不要になったファイルの削除**
|
|
|
+ - `stores/ui.tsx` の移行済み部分を削除
|
|
|
+ - `stores/modal.tsx` の移行済み部分を削除
|
|
|
+
|
|
|
+2. **統一的なパターンの確立**
|
|
|
+ - 命名規則の統一
|
|
|
+ - ディレクトリ構造の整理
|
|
|
+ - ドキュメントの更新
|
|
|
+
|
|
|
+### 📋 移行時の注意点
|
|
|
+
|
|
|
+### SSR対応パターン(ハイドレーション専用モジュール方式)
|
|
|
+```typescript
|
|
|
+// states/hydrate/sidebar.ts - サイドバー状態のSSRハイドレーション
|
|
|
+export const useHydrateSidebarAtoms = (sidebarConfig: ISidebarConfig, userUISettings?: IUserUISettings): void => {
|
|
|
+ useHydrateAtoms([
|
|
|
+ [preferCollapsedModeAtom, userUISettings?.preferCollapsedModeByUser ?? sidebarConfig.isSidebarCollapsedMode],
|
|
|
+ // 他のサイドバー関連 atom も同様に追加
|
|
|
+ ]);
|
|
|
+};
|
|
|
+
|
|
|
+// 各ページでの使用(シンプル!)
|
|
|
+const MyPage = (props) => {
|
|
|
+ useHydrateSidebarAtoms(props.sidebarConfig, props.userUISettings);
|
|
|
+ // 他のロジック...
|
|
|
+ return <div>...</div>;
|
|
|
+};
|
|
|
+
|
|
|
+// 将来の拡張例
|
|
|
+// states/hydrate/modal.ts
|
|
|
+// states/hydrate/editor.ts
|
|
|
+```
|
|
|
+
|
|
|
+**ディレクトリ構造:**
|
|
|
+```
|
|
|
+states/
|
|
|
+├── ui/
|
|
|
+│ ├── sidebar.ts # サイドバー状態の定義・操作
|
|
|
+│ ├── modal.ts # モーダル状態の定義・操作
|
|
|
+│ └── editor.ts # エディター状態の定義・操作
|
|
|
+└── hydrate/
|
|
|
+ ├── sidebar.ts # サイドバー状態のSSRハイドレーション
|
|
|
+ ├── modal.ts # モーダル状態のSSRハイドレーション (将来)
|
|
|
+ └── editor.ts # エディター状態のSSRハイドレーション (将来)
|
|
|
+```
|
|
|
+
|
|
|
+### 永続化パターン
|
|
|
+```typescript
|
|
|
+// 永続化が必要な状態の実装パターン
|
|
|
+const someSettingAtom = atom(defaultValue);
|
|
|
+const someSettingAtomExt = atom(
|
|
|
+ get => get(someSettingAtom),
|
|
|
+ (get, set, update: ValueType) => {
|
|
|
+ set(someSettingAtom, update);
|
|
|
+ scheduleToPut({ settingKey: update });
|
|
|
+ },
|
|
|
+);
|
|
|
+```
|
|
|
+
|
|
|
+### 初期化パターン
|
|
|
+```typescript
|
|
|
+// サーバーサイドデータでの初期化パターン
|
|
|
+const initializedAtom = atom(false);
|
|
|
+export const useSomeSettingInitializer = (initialData: ValueType): void => {
|
|
|
+ const [isInitialized, setIsInitialized] = useAtom(initializedAtom);
|
|
|
+ const [, setSomeSetting] = useSomeSetting();
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ if (!isInitialized) {
|
|
|
+ setSomeSetting(initialData);
|
|
|
+ setIsInitialized(true);
|
|
|
+ }
|
|
|
+ }, [isInitialized, setSomeSetting, setIsInitialized, initialData]);
|
|
|
+};
|
|
|
+```
|
|
|
+
|
|
|
+### テスト戦略
|
|
|
+- 各 atom の単体テスト
|
|
|
+- 永続化機能のテスト
|
|
|
+- コンポーネント結合テスト
|
|
|
+- E2E テストでの動作確認
|
|
|
+
|
|
|
+## 🔍 判断基準
|
|
|
+
|
|
|
+### Jotai に移行すべき状態
|
|
|
+- ✅ クライアントサイド完結の UI 状態
|
|
|
+- ✅ 同期的な状態更新
|
|
|
+- ✅ シンプルなデータ構造
|
|
|
+- ✅ コンポーネント間での状態共有が必要
|
|
|
+
|
|
|
+### SWR を継続使用すべき状態
|
|
|
+- ❌ サーバーからのデータフェッチが必要
|
|
|
+- ❌ 非同期的な状態更新
|
|
|
+- ❌ キャッシュ機能が重要
|
|
|
+- ❌ リアルタイム更新が必要
|