0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

編集ダイアログは save → refresh → close の順 — リスト列の表示漏れを構造的に防ぐ

0
Posted at

この記事は約 4 分で読めます。

筆者プロフィール: ソフトウェアエンジニア。「知った気にならない。いつまでも学び続ける」を信条に、業務と個人開発の両輪で技術を磨いています。AI 駆動開発で複数の個人開発アプリを構築・運用中。
👉 ポートフォリオ: 筆者ホームページ

「保存したのに、リスト画面で古い値が一瞬見える」 — 編集ダイアログの 保存 → 閉じる順序ミス で起きる UX 事故です。本記事では、運用中の SaaS 「たすきば Knowledge Relay」 で確立した save → refresh → close の正しい順序を整理します (KDD §5.11)。

サービスの機能紹介・画面イメージ・コンセプトは公式プロダクトページをご覧ください。
👉 たすきば Knowledge Relay — 公式プロダクトページ

どう間違えるか

最初の実装でハマったケース:

// ✗ NG: 閉じるのが早すぎる
async function handleSave() {
  await saveKnowledge(data);  // ① 保存
  onClose();                    // ② ダイアログを閉じる
  router.refresh();              // ③ ページ再評価
}

一見、自然な順序に見えます。

しかし:

② でダイアログが閉じる
③ で router.refresh() が走るが、Server Component の再評価には時間がかかる
ユーザが見ているリスト画面で、まだ古いデータが表示されている

ユーザ体験: 「保存したのに、リストに反映されない」(数秒後に反映)。


1. 正しい順序

// ✓ OK: refresh を待ってから閉じる
async function handleSave() {
  await saveKnowledge(data);  // ① 保存 (server action)
  router.refresh();              // ② Server Component を再評価
  onClose();                    // ③ ダイアログを閉じる
}

router.refresh()onClose() に呼ぶ。

① DB に保存完了
② Server Component が再評価され、最新データを取得
③ ダイアログを閉じる (リストにはすでに最新データが表示)

ユーザ体験: 「保存 → 即座にリストが更新 → ダイアログ閉じる」。


2. なぜ refresh を待つ必要があるか

router.refresh()非同期。内部的には:

1. Next.js が Server Component を再評価
2. 新しい HTML / RSC payload をクライアントに送る
3. React がコンポーネントを再レンダリング

これに 100ms〜数秒 かかります。

onClose() を refresh の前に呼ぶと:

シナリオ 結果
ダイアログ閉じる 古いリストが一瞬見える
その後 refresh 完了 新しいリストに置き換わる

「チラッと古いデータが見える」 UI 体験になります。


3. Optimistic Update のパターン

UI 体験をさらに改善するなら、Optimistic Update:

async function handleSave() {
  // 1. UI を即座に更新 (Optimistic)
  setLocalKnowledge(updatedData);

  // 2. ダイアログを即座に閉じる
  onClose();

  // 3. 実際の保存 (バックグラウンド)
  try {
    await saveKnowledge(updatedData);
    router.refresh();
  } catch (err) {
    setLocalKnowledge(previousData);  // ロールバック
    toast.error('保存に失敗しました');
  }
}

ただし、ロールバック処理が複雑 になるため、たすきばは基本的に Optimistic を使わず、シンプルな順序を採用しています。


4. 部分更新による表示漏れ

KDD §5.11 では、別の罠を記録:

編集ダイアログで一部のフィールドだけ更新したとき、リスト列で その他のフィールドが表示されない ことがある。

現象 原因
一部の列だけ更新反映 ダイアログ内で更新された値だけが props
その他の列が古い リスト側は refresh 前のデータ
対策 内容
router.refresh() を必ず呼ぶ Server Component 全体を再評価
部分的な local state 更新は避ける 全画面リフレッシュを徹底

5. 失敗時の挙動

保存が失敗したら:

async function handleSave() {
  setSaving(true);
  try {
    await saveKnowledge(data);
    router.refresh();
    onClose();
  } catch (err) {
    toast.error(`保存に失敗しました: ${err.message}`);
    // ダイアログは閉じない、入力値は残る
  } finally {
    setSaving(false);
  }
}
ルール 内容
ダイアログを 閉じない ユーザが値を失わない
エラーメッセージ表示 ユーザが原因を理解
「もう一度試す」 再試行可能

ユーザの入力を絶対に失わない。UX の基本ですが、最初の実装ではしばしば見落とされます。


6. 保存中の UI

保存中はユーザに状態を伝えます。

<Button onClick={handleSave} disabled={saving}>
  {saving ? '保存中...' : '保存'}
</Button>
効果 内容
重複保存リクエスト防止 disabled で二重クリック防止
処理中の可視化 ユーザに「処理中」と分かる

7. Server Action + revalidatePath

// src/services/knowledge.actions.ts
'use server';
export async function saveKnowledgeAction(data: KnowledgeUpdateInput) {
  await updateKnowledge(data);
  revalidatePath('/projects/X/knowledge');  // サーバ側で revalidate
}

Server Action 内で revalidatePath() を呼ぶことで、サーバ側でキャッシュが無効化。クライアントの router.refresh() と組み合わせて、確実に最新化。


8. 汎化ルール

KDD §5.11 で汎化ルール化:

編集ダイアログの保存は、必ず以下の順序で行う:

  1. Server Action で DB 保存
  2. router.refresh() を await
  3. onClose() を呼ぶ

このルールを CONTRIBUTING.md にも書き、新規ダイアログ実装時の参照 にします。


9. E2E でのテスト

test('編集ダイアログの保存後、リストに即時反映される', async ({ page }) => {
  await page.click('button:has-text("編集")');
  await page.fill('input[name="title"]', '新しいタイトル');
  await page.click('button:has-text("保存")');

  await page.waitForSelector('[role="dialog"]', { state: 'hidden' });
  await expect(page.locator('text=新しいタイトル')).toBeVisible();
});

このテストが green であれば、save → refresh → close の順序が正しい


おわりに

ルール 効果
save → refresh → close の順序 表示漏れを防ぐ
保存中は disabled 二重クリック防止
失敗時にダイアログを閉じない 入力値を失わない
Server Action + revalidatePath サーバキャッシュ無効化
E2E でリスト反映を verify リグレッション防止

ダイアログ実装の 小さな順序ミスが、ユーザ体験を大きく左右 します。KDD §5.11 で構造化したルールが、今のたすきばを支えています。

本記事のルールは、運用中の SaaS 「たすきば Knowledge Relay」 で実装しています。
👉 たすきば Knowledge Relay — 公式プロダクトページ

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?