useOptimistic 是 React19 提供了一个新的 hooks,他可以实现乐观更新,使得数据更新效果更加友好。

乐观更新:在进行服务器请求接口时,先在用户界面上预先处理成功的结果(假设操作成功),然后再发送请求到服务器进行确认。如果服务器返回错误,前端再进行相应的回滚或错误处理。

useOptimistic 的语法

const [optimisticState, addOptimistic] = useOptimistic(state, updateFn);

useOptimistic 接收两个参数:

  • state:初始状态
  • updateFn:一个函数,接收当前状态和乐观更新值,返回新一个和当前状态和新值合并的新状态。

useOptimistic 返回值:

  • optimisticState:是 updateFn 返回的新状态 optimisticState
  • addOptimisticaddOptimistic 是需要乐观更新时要调用的函数。它接受一个状态,会传递给 updateFn 的第二个参数,返回新状态 optimisticValue

上述的描述可能有点抽象,我们进行场景模拟来解释下该 hook 的使用。

举个例子

我们直接举一个实际应用的例子来看,现在要实现一个评论列表,每次新增评论时,会立刻把结果显示在列表中,并同步到服务器,但是,如果服务器返回错误,则进行回滚提示。我们先来创建一个 App 组件。

App组件

export default function App() {
  const [comments, setComments] = useState([{ text: "第一条评论", sending: false }]);

  async function addComment(formData) {
    const newComment = await postComment(formData.get("comment"));
    setComments((comments) => [...comments, { text: newComment }]);
  }

  return <CommentSection comments={comments} addComment={addComment} />;
}

CommentSection组件

我们要实现一个评论组件,包含列表展示功能和评论表单提交功能。

function CommentSection({ comments, addComment }) {
  const formRef = useRef();

  const [optimisticComments, addOptimisticComment] = useOptimistic(comments, (state, newComment) => [
    ...state,
    {
      text: newComment,
      sending: true,
      failed: false,
    },
  ]);

  async function handleFormSubmit(formData) {
    const newComment = formData.get("comment");
    addOptimisticComment(newComment); // 乐观更新
    formRef.current.reset();
    try {
      await addComment(formData);
    } catch (error) {
      handleFailure(newComment);
    }
  }

  function handleFailure(failedComment) {
    // 提示失败
  }

  return (
    <>
      <div>
        {optimisticComments.map((comment, index) => (
          <div key={index} style={{ color: comment.failed ? "red" : "black" }}>
            {comment.text}
            {!!comment.sending && <small> (发送中...)</small>}
          </div>
        ))}
      </div>
      <form action={handleFormSubmit} ref={formRef}>
        <input type="text" name="comment" placeholder="评论..." />
        <button type="submit">提交</button>
      </form>
    </>
  );
}

最终效果