从工作原理入手理解React十一:useOptimistic

50 阅读1分钟

什么是useOptimistic

useOptimistic 是React 19引入的新hook,用于实现乐观更新,允许我们在等待异步操作完成时立即更新UI,让用户对异步无感,提供更好的用户体验。


为什么需要useOptimistic

在一些场景下,需要在异步请求完成前立即更新UI来提升用户体验,如点赞、收藏等操作。useOptimistic简化了乐观更新的实现,在useOptimistic之前想要实现乐观更新,我们需要大量额外的代码判断控制更新流程和改变状态。useOptimistic能够根据我们是否调用addOptimistic来决定返回的组件状态实现UI切换。


基本使用

useOptimistic 接收一个state和updateFn函数,返回optimisticState和addOptimistic函数。

state:初始状态

updateFn:更新函数,接收state和optimisticValue,返回结果乐观状态,乐观状态是state和optimisticValue的合并值。必须是纯函数。

optimisticState:结果乐观状态,如果有操作挂起,它为state和optimisticValue的合并值,否则为state

addOptimistic:触发乐观更新时调用的dispatch函数,接收optimisticValue,并调用updateFn,触发更新

import { useOptimistic, useState, useRef, startTransition } from "react";
interface Message {
  text: string;
  sending: boolean;
  key: number;
}

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

function Thread({ messages, sendMessageAction }: { messages: Message[], sendMessageAction: (formData: FormData) => Promise<void> }) {
  const formRef = useRef<HTMLFormElement>(null);
  function formAction(formData: FormData) {
    addOptimisticMessage(formData.get("message") as string);
    formRef.current?.reset();
    startTransition(async () => {
      await sendMessageAction(formData);
    });
  }
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (state, newMessage: string) => [
      {
        text: newMessage,
        sending: true,
        key: state.length + 1
      },
      ...state,
    ]
  );

  return (
    <>
      <form action={formAction} ref={formRef}>
        <input type="text" name="message" placeholder="你好!" />
        <button type="submit">发送</button>
      </form>
      {optimisticMessages.map((message, index) => (
        <div key={index}>
          {message.text}
          {!!message.sending && <small>(发送中……)</small>}
        </div>
      ))}
    
    </>
  );
}

export default function App() {
  const [messages, setMessages] = useState([
    { text: "你好,在这儿!", sending: false, key: 1 }
  ]);
  async function sendMessageAction(formData: FormData) {
    const sentMessage = await deliverMessage(formData.get("message") as string);
    startTransition(() => {
      setMessages((messages) => [{ text: sentMessage, sending: false, key: messages.length+1 }, ...messages]);
    })
  }
  return <Thread messages={messages} sendMessageAction={sendMessageAction} />;
}