React19にuseOptimicが出て楽観的更新ができるようになった

## はじめに
React19で新たに追加された`useOptimistic`フックは、開発者が楽観的なUI更新を簡単に実現できる強力なフックです
本記事では、この新フックの基本的な説明、使用例、利点と注意点、React18以前の実装方法との比較を紹介します

## 楽観的更新とは
ユーザーの操作に対して非同期処理の完了を待たずにUIを更新する手法のことです
楽観的更新によりユーザーの操作に対して即座にフィードバックを提供できるため、UXの向上につながります

## `useOptimistic`フックの基本的な説明
`useOptimistic`フックは次のように使用します
```jsx
const [optimisticState, updateOptimisticState] = useOptimistic(initialState, (currentState, optimisticValue) => {
 // actionに基づいてstateを楽観的更新するロジック
 return newState;
});
```

- `initialState`
    - 初期状態を指定します
- 第２引数
    - 現在の状態とアクションを受け取り、新しい状態を返す関数を定義します

## useOptimisticの使用例
次に、`useOptimistic`を使った簡単な例を紹介します
下記では、リストアイテムを追加する場合の実装です

```jsx
import { useOptimistic, useState, useRef } from "react";

async function deliverMessage(message) {
  await new Promise((res) => setTimeout(res, 1000));
  return message;
}

function Thread({ messages, sendMessage }) {
  const formRef = useRef();
  // フォームから送信されたデータを引数にaddOptimisticMessageを実行
  async function formAction(formData) {
    addOptimisticMessage(formData.get("message"));
    formRef.current.reset();
    await sendMessage(formData);
  }
  // 第１引数は結果、第２引数は関数名
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages, // 下記の処理を終えるまではmessages（addOptimisticMessageに代入されている値）が出力される
    (state, newMessage) => [
      ...state,
      {
        text: newMessage,
        sending: true
      }
    ]
  );

  return (
    <>
      {optimisticMessages.map((message, index) => (
        <div key={index}>
          {message.text}
          {!!message.sending && <small> (Sending...)</small>}
        </div>
      ))}
      <form action={formAction} ref={formRef}>
        <input type="text" name="message" placeholder="Hello!" />
        <button type="submit">Send</button>
      </form>
    </>
  );
}

export default function App() {
  const [messages, setMessages] = useState([
    { text: "Hello there!", sending: false, key: 1 }
  ]);
  async function sendMessage(formData) {
    const sentMessage = await deliverMessage(formData.get("message"));
    setMessages((messages) => [...messages, { text: sentMessage }]);
  }
  return <Thread messages={messages} sendMessage={sendMessage} />;
}
```

上記は公式のサンプルコードを１つのファイルに集約しました
入力したテキストを送信すると、送信中でも表示されていることがわかります

## `useOptimistic`を使った際の利点と注意点
#### 利点
- 即時フィードバック
    - ユーザーがアクションを起こした際に即座にUIが更新されるため、レスポンスが速く感じられます
- シンプルなコード
    - 楽観的な更新を簡単に実装でき、コードがシンプルになります

#### 注意点
- 一貫性の管理
    - サーバー側の状態とクライアント側の状態が一致しない場合があるため、一貫性の管理が重要です
        - 問い合わせ対応の際に送信中に起きたはずの不具合が送信後に起きていた
        - 上記のようなことが想定されます
- エラーハンドリング
    - サーバーへのリクエストが失敗した場合のエラーハンドリングが必要です


## React18以前の実装方法と比較
先ほどのサンプルコードをReact18にしようとするとかなり複雑になってしまいます
手動で状態を管理する必要があり、コードが複雑になり、ネストも多くなってしまいます


:::note
<details><summary >React18以前のサンプルコード</summary>

```jsx
import React, { useState, useRef } from "react";

async function deliverMessage(message) {
  await new Promise((res) => setTimeout(res, 1000));
  return message;
}

function Thread({ messages, sendMessage }) {
  const formRef = useRef();
  const [optimisticMessages, setOptimisticMessages] = useState(messages);

  async function formAction(formData) {
    const newMessage = formData.get("message");
    addOptimisticMessage(newMessage);
    formRef.current.reset();
    await sendMessage(newMessage);
  }

  function addOptimisticMessage(newMessage) {
    // 楽観的更新のために新しいメッセージを手動で追加
    setOptimisticMessages((state) => [
      ...state,
      {
        text: newMessage,
        sending: true
      }
    ]);
  }

  return (
    <>
      {optimisticMessages.map((message, index) => (
        <div key={index}>
          {message.text}
          {!!message.sending && <small> (Sending...)</small>}
        </div>
      ))}
      <form
        onSubmit={(e) => {
          e.preventDefault();
          const formData = new FormData(formRef.current);
          formAction(formData);
        }}
        ref={formRef}
      >
        <input type="text" name="message" placeholder="Hello!" />
        <button type="submit">Send</button>
      </form>
    </>
  );
}

export default function App() {
  const [messages, setMessages] = useState([
    { text: "Hello there!", sending: false, key: 1 }
  ]);

  async function sendMessage(newMessage) {
    const sentMessage = await deliverMessage(newMessage);
    setMessages((messages) =>
      messages.map((message) =>
        // メッセージの送信状態を手動で管理
        message.text === newMessage && message.sending
          ? { ...message, text: sentMessage, sending: false }
          : message
      )
    );
  }

  return <Thread messages={messages} sendMessage={sendMessage} />;
}

```
</details>
:::
`useOptimistic`を使うことでこの処理を簡素化できます

## まとめ
`useOptimistic`フックは、楽観的なUI更新を簡単に実装するための強力なフックです
即時フィードバックの提供やコードの簡素化を実現し、ユーザー体験を向上させるでしょう
楽観的更新という概念を初めて知ったので、今後使ってみようと思います

## 参考リンク

https://ja.react.dev/reference/react/useOptimistic

https://zenn.dev/uhyo/books/react-19-new/viewer/use-optimistic#%E3%81%AA%E3%81%9C%E9%96%A2%E6%95%B0%E3%81%A7%E6%A5%BD%E8%A6%B3%E7%9A%84%E6%9B%B4%E6%96%B0%E3%82%92%E8%A1%8C%E3%81%86%E3%81%AE%E3%81%8B

https://azukiazusa.dev/blog/react-use-optimistic-hook/