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?

楽観的UI更新について

Posted at

楽観的UIとは?

楽観的UIとは、ユーザーの操作が完了する前に、その結果を即座に画面に反映させる技術です。通常、データがサーバーと同期されるまでの待ち時間を短縮し、ユーザー体験を向上させることが目的です。操作後にすぐフィードバックを得られるため、特にレスポンスの遅いAPIや不安定なネットワーク下でも快適なユーザー体験を提供できます。

Reactで楽観的UIを実装する際、以下の流れで進めます。

  • ローカル状態を即座に更新
  • APIリクエストを送信
  • レスポンスに基づき最終的な状態を確定

では、具体的にどのように楽観的UIを実装するのか、シンプルな例で見ていきましょう。

例:タスク管理アプリにおける楽観的UI

前提

サーバーからタスクを取得し、追加・更新・削除を行うシンプルなタスク管理アプリを例にします。

1. タスクの追加機能

まず、タスクを追加するUIを考えます。通常のタスク追加処理では、サーバーにリクエストを送信し、そのレスポンスを待って画面を更新しますが、楽観的UIではそのリクエストの前に画面を更新します。

import { useState } from 'react';
import { supabase } from './supabaseClient';
import { Task } from './types';

type Task = {
  id: number;
  title: string;
  status: 'pending' | 'complete';
};

function TaskList() {
  const [tasks, setTasks] = useState<Task[]>([]);

  const addTask = async (title: string) => {
    // 1. 楽観的にローカル状態を更新
    const newTask: Task = { id: Date.now(), title, status: 'pending' };
    setTasks([...tasks, newTask]);

    try {
      // 2. サーバーにデータを送信
      const { data, error } = await supabase.from('tasks').insert([{ title }]);
      if (error) throw error;

      // 3. サーバーからのレスポンスに基づいて状態を更新
      setTasks((prevTasks) =>
        prevTasks.map((task) =>
          task.id === newTask.id ? { ...task, id: data[0].id, status: 'complete' } : task
        )
      );
    } catch (error) {
      setTasks((prevTasks) => prevTasks.filter((task) => task.id !== newTask.id));
      console.error('Error adding task:', error);
    }
  };

  return (
    <div>
      <h1>Task List</h1>
      <button onClick={() => addTask('New Task')}>Add Task</button>
      <ul>
        {tasks.map((task) => (
          <li key={task.id}>{task.title} ({task.status})</li>
        ))}
      </ul>
    </div>
  );
}

export default TaskList;
実装のポイント

楽観的な状態更新
APIリクエストを行う前に、ローカル状態に新しいタスクを即座に追加します。この段階では、サーバーのレスポンスを待たず、あくまで「楽観的」にUIを更新します。

APIリクエストの処理
Supabaseを使ってサーバーに新しいタスクを送信します。サーバーからのレスポンスが返ってきた際に、タスクIDやステータスを更新して、最終的なデータをローカル状態に反映させます。

エラーハンドリング
万が一サーバー側でエラーが発生した場合、ローカルで追加したタスクを削除して、楽観的な更新を取り消します。これにより、ユーザーに不正なデータが表示され続けることを防ぎます。

2. 削除機能

同様に、タスクを削除する際にも楽観的UIを適用できます。

const deleteTask = async (taskId: number) => {
  // 1. ローカル状態から即座にタスクを削除
  const previousTasks = tasks;
  setTasks(tasks.filter((task) => task.id !== taskId));

  try {
    // 2. サーバーに削除リクエストを送信
    const { error } = await supabase.from('tasks').delete().eq('id', taskId);
    if (error) throw error;
  } catch (error) {
    // エラーハンドリング(削除失敗時は元の状態に戻す)
    setTasks(previousTasks);
    console.error('Error deleting task:', error);
  }
};

楽観的UIのメリット

レスポンス速度の向上: APIリクエストの処理時間を待たずに、即座にUIが更新されるため、ユーザーの操作感が向上します。
ユーザーエクスペリエンスの向上: サーバーとの通信遅延を感じさせないスムーズな体験を提供できます。

楽観的UIのデメリット

サーバーとの不整合の可能性: サーバーとの通信が失敗した場合、表示されている内容が実際のデータと一致しなくなる可能性があります。このため、エラーハンドリングやロールバック処理が重要です。

まとめ

Reactで楽観的UIを実装することで、ユーザー体験を大幅に向上させることができます。特に、APIのレスポンスが遅い場合や、リアルタイム性が求められるアプリケーションでは有効です。

最後に

他にも色々な記事を書いているので、よければ読んでいってください!

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?