useOptimistic
は、UIを楽観的に更新するためのReactフックです。
const [optimisticState, addOptimistic] = useOptimistic(state, updateFn);
リファレンス
useOptimistic(state, updateFn)
useOptimistic
は、非同期アクション実行中に異なる状態を表示するためのReactフックです。引数として状態を受け取り、ネットワークリクエストなどの非同期アクション実行中は異なる状態のコピーを返します。現在の状態とアクションへの入力を取得し、アクションが保留中の間に使用される楽観的な状態を返す関数を提供します。
この状態は、アクションが実際に完了するまでに時間がかかる場合でも、アクションの実行結果をユーザーにすぐに提示するために使用されることが多いため、「楽観的」状態と呼ばれます。
import { useOptimistic } from 'react';
function AppContainer() {
const [optimisticState, addOptimistic] = useOptimistic(
state,
// updateFn
(currentState, optimisticValue) => {
// merge and return new state
// with optimistic value
}
);
}
パラメータ
state
: 初期に、そしてアクションが保留されていないときはいつでも返される値。updateFn(currentState, optimisticValue)
: 現在の状態とaddOptimistic
に渡される楽観的な値を取得し、結果として得られる楽観的な状態を返す関数。純粋関数でなければなりません。updateFn
は2つのパラメータを受け取ります。currentState
とoptimisticValue
です。戻り値はcurrentState
とoptimisticValue
の統合された値になります。
戻り値
optimisticState
: 結果として得られる楽観的な状態。アクションが保留されていない限りstate
と等しく、保留中の場合はupdateFn
が返す値と等しくなります。addOptimistic
:addOptimistic
は、楽観的な更新がある場合に呼び出すディスパッチ関数です。任意の型の引数optimisticValue
を1つ取得し、state
とoptimisticValue
を使用してupdateFn
を呼び出します。
使用方法
楽観的フォーム更新
useOptimistic
フックは、ネットワークリクエストなどのバックグラウンド操作が完了する前に、ユーザーインターフェースを楽観的に更新する方法を提供します。フォームのコンテキストでは、このテクニックにより、アプリがより応答性が高くなります。ユーザーがフォームを送信すると、サーバーからの応答を待って変更を反映する代わりに、期待される結果でインターフェースがすぐに更新されます。
例えば、ユーザーがフォームにメッセージを入力して「送信」ボタンを押すと、useOptimistic
フックにより、メッセージが実際にサーバーに送信される前でも、「送信中…」ラベル付きでリストにすぐに表示されます。この「楽観的」なアプローチは、速度と応答性の印象を与えます。その後、フォームはバックグラウンドでメッセージの送信を試みます。サーバーがメッセージの受信を確認すると、「送信中…」ラベルが削除されます。
import { useOptimistic, useState, useRef } from "react"; import { deliverMessage } from "./actions.js"; function Thread({ messages, sendMessage }) { const formRef = useRef(); async function formAction(formData) { addOptimisticMessage(formData.get("message")); formRef.current.reset(); await sendMessage(formData); } const [optimisticMessages, addOptimisticMessage] = useOptimistic( messages, (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} />; }