React19新特性,如何理解乐观更新钩子useOptimistic?
发布于 作者:苏南大叔 来源:程序如此灵动~ 我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...
本文说一下React19
的新特性,乐观更新钩子useOptimistic()
。这个效果的是使用场景,非常常见。比如:在聊天对话的时候,点击发送按钮,消息会立刻出现在聊天列表里面。或者在发送朋友圈的时候,即使提交的是个视频文件也会立刻出现在时间轴里面,这个就是乐观更新。
苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。本文测试环境:nodejs@20.18.0
,create-react-app@5.0.1
,react@19.0.0
。乐观更新基于的事实是:绝大多数的情况下,都会更新成功的,更新失败的概率非常小。
一套组合逻辑
乐观更新是一套组合逻辑,并不是单独的useOptimistic()
就能够完成的。它只是对数据状态值的一个临时副本修改。在适当的时候,会对副本数据进行对齐修正而已。
官方文档:
函数原型:
const [optimisticState, addOptimistic] = useOptimistic(state, updateFn);
optimisticState
,这个是用于更新界面UI
的那个临时数据副本,被“乐观”更新的那个数据就在这里。addOptimistic
,这是个用于调用的更新函数,就是启动“乐观”更新的那个函数。它仅仅是个调用,具体的逻辑还是要看updateFn
这个函数。state
,它就是react
的state
状态值。也是optimisticState
的原型值,非副本。它一般由useState()
来获取提供数据源。(当然也不排除其它的数据源可能性)。updateFn
,它是一个具体的函数逻辑。接收参数为:当前state
和被更新的数据(addOptimistic
传入的第一个参数)。
optimisticState
的真身是state
,optimisticState
仅仅是副本。副本更新和真身更新冲突的时候,副本失效。真身更新覆盖副本更新。
官方例子
下面的代码改编自官方范例,效果和各大聊天软件里面的对话框效果很相似。提交按钮就“乐观”更新上去了,用户体验非常好。因为几乎没有延迟。正常逻辑的话,还需要一两秒的数据等待的过程。
import { useOptimistic, useState, useRef } from "react";
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
async function serverAPI(message) {
let tmp = getRandomInt(1, 3) * 1000;
await new Promise((res) => setTimeout(res, tmp));
return message + "."; // 这个return很重要,被合并到真实state里面,进而影响乐观state
}
function Main({ messages, addRealMessageFn }) {
const formRef = useRef(null);
let aaa = useRef(messages.length);
async function formAction(formData) {
aaa.current++;
let message = formData.get("message") + aaa.current.toString();
addOptimisticMessage(message);
formRef.current.reset();
await addRealMessageFn(message);
}
const [optimisticMessages, addOptimisticMessage] = useOptimistic(
messages,
(state, newMessage) => [
...state,
{
text: newMessage,
sending: true,
},
]
);
return (
<>
<form action={formAction} ref={formRef}>
<input
type="text"
name="message"
placeholder="你好!"
defaultValue={"sunan"}
/>
<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: 0 },
{ text: "hola,Uncle Sunan", sending: false, key: 1 },
]);
async function addRealMessageFn(message) {
const okMessage = await serverAPI(message);
setMessages((messages) => [...messages, { text: okMessage }]);
}
return <Main messages={messages} addRealMessageFn={addRealMessageFn} />;
}
存在的问题
目前来说,如果快速连续点击提交按钮的话,会发现提交的结果列表中会有重复数据,并且最终的结果列表是混乱的。待后续文章探讨更新。
相关文章
这个钩子的效果,其实和前面苏南大叔一直写的文章里面的pending
效果完全相反。以前的文章是拼命让用户知道这个和服务器端的接口交互过程,pending
就是交互。而这个乐观更新,就是尽量不让用户感受到这个数据交互的延迟。延迟客观存在,但是因为大概率情况下,不会出错。所以,就提前把效果展现出来了。这就是乐观更新。
- https://newsn.net/say/react-useactionstate.html
- https://newsn.net/say/react-useactionstate-2.html
- https://newsn.net/say/react-useformstatus.html
如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。