useOptimistic 是一个 React Hook,它允许你乐观地更新用户界面。
const [optimisticState, setOptimistic] = useOptimistic(value, reducer?);参考
🌐 Reference
useOptimistic(value, reducer?)
在组件的顶层调用 useOptimistic 来为某个值创建乐观状态。
🌐 Call useOptimistic at the top level of your component to create optimistic state for a value.
import { useOptimistic } from 'react';
function MyComponent({name, todos}) {
const [optimisticAge, setOptimisticAge] = useOptimistic(28);
const [optimisticName, setOptimisticName] = useOptimistic(name);
const [optimisticTodos, setOptimisticTodos] = useOptimistic(todos, todoReducer);
// ...
}参数
🌐 Parameters
value:当没有待处理的操作时返回的值。- 可选
reducer(currentState, action):指定乐观状态如何更新的 reducer 函数。它必须是纯函数,应该接收当前状态和 reducer action 参数,并且应该返回下一个乐观状态。
返回
🌐 Returns
useOptimistic 返回一个包含恰好两个值的数组:
optimisticState:当前的乐观状态。它等于value,除非有一个操作正在等待,此时它等于reducer返回的状态(如果没有提供reducer,则等于传递给 set 函数的值)。set函数 允许你在一个 Action 内将乐观状态更新为不同的值。
set 的功能,类似于 setOptimistic(optimisticState)
🌐 set functions, like setOptimistic(optimisticState)
useOptimistic 返回的 set 函数允许你在 Action 的持续期间更新状态。你可以直接传递下一个状态,或者传递一个根据前一个状态计算下一个状态的函数:
🌐 The set function returned by useOptimistic lets you update the state for the duration of an Action. You can pass the next state directly, or a function that calculates it from the previous state:
const [optimisticLike, setOptimisticLike] = useOptimistic(false);
const [optimisticSubs, setOptimisticSubs] = useOptimistic(subs);
function handleClick() {
startTransition(async () => {
setOptimisticLike(true);
setOptimisticSubs(a => a + 1);
await saveChanges();
});
}参数
🌐 Parameters
optimisticState:你希望在Action期间乐观状态的值。如果你向useOptimistic提供了reducer,此值将作为第二个参数传递给你的 reducer。它可以是任何类型的值。- 如果你将一个函数作为
optimisticState传入,它将被视为一个_更新器函数_。它必须是纯函数,应仅以待处理状态作为唯一参数,并应返回下一个乐观状态。React 会将你的更新器函数放入队列中并重新渲染你的组件。在下一次渲染过程中,React 将通过将排队的更新器应用于前一个状态来计算下一个状态,这类似于useState更新器 的处理方式。
- 如果你将一个函数作为
返回
🌐 Returns
set 函数没有返回值。
注意事项
🌐 Caveats
set函数必须在 Action 内部调用。如果你在 Action 外部调用该 setter,React 会显示警告,并且乐观状态会短暂渲染。
深入研究
🌐 How optimistic state works
useOptimistic 允许你在操作进行中显示一个临时值:
const [value, setValue] = useState('a');
const [optimistic, setOptimistic] = useOptimistic(value);
startTransition(async () => {
setOptimistic('b');
const newValue = await saveChanges('b');
setValue(newValue);
});当在 Action 内调用 setter 时,useOptimistic 将触发重新渲染以显示在 Action 执行过程中该状态。否则,传递给 useOptimistic 的 value 将被返回。
🌐 When the setter is called inside an Action, useOptimistic will trigger a re-render to show that state while the Action is in progress. Otherwise, the value passed to useOptimistic is returned.
这种状态被称为“乐观的”,因为它用于立即向用户呈现执行某个操作的结果,即使该操作实际上需要时间才能完成。
🌐 This state is called the “optimistic” because it is used to immediately present the user with the result of performing an Action, even though the Action actually takes time to complete.
更新流程
- 立即更新:当调用
setOptimistic('b')时,React 会立即使用'b'进行渲染。 - (可选)在 Action 中使用 await:如果你在 Action 中使用 await,React 会继续显示
'b'。 - 过渡已安排:
setValue(newValue)安排对实际状态的更新。 - (可选)等待 Suspense:如果
newValue被挂起,React 会继续显示'b'。 - 单次渲染提交:最终,
newValue对value和optimistic的提交。
没有额外的渲染来“清除”乐观状态。当过渡完成时,乐观状态和真实状态会在同一次渲染中收敛。
🌐 There’s no extra render to “clear” the optimistic state. The optimistic and real state converge in the same render when the Transition completes.
最终状态如何确定
useOptimistic 的 value 参数决定操作完成后显示的内容。其工作方式取决于你使用的模式:
🌐 The value argument to useOptimistic determines what displays after the Action finishes. How this works depends on the pattern you use:
- 硬编码的值 如
useOptimistic(false):操作之后,state仍然是false,所以界面显示false。这对于总是从false开始的待处理状态非常有用。 - 传入的 Props 或 state,如
useOptimistic(isLiked):如果父组件在动作期间更新isLiked,动作完成后使用的新值。UI 就是通过这种方式反映动作的结果。 - Reducer 模式 类似于
useOptimistic(items, fn):如果在 Action 挂起期间items发生变化,React 会使用新的items重新运行你的reducer来重新计算状态。这可以让你的乐观更新保持在最新数据之上。
当操作失败时会发生什么
如果 Action 抛出错误,Transition 仍然会结束,并且 React 会使用当前的 value 进行渲染。由于父组件通常只在成功时更新 value,失败意味着 value 没有改变,所以 UI 会显示乐观更新之前的内容。你可以捕获错误以向用户显示信息。
🌐 If the Action throws an error, the Transition still ends, and React renders with whatever value currently is. Since the parent typically only updates value on success, a failure means value hasn’t changed, so the UI shows what it showed before the optimistic update. You can catch the error to show a message to the user.
用法
🌐 Usage
向组件添加乐观状态
🌐 Adding optimistic state to a component
在组件的顶层调用 useOptimistic 来声明一个或多个乐观状态。
🌐 Call useOptimistic at the top level of your component to declare one or more optimistic states.
import { useOptimistic } from 'react';
function MyComponent({age, name, todos}) {
const [optimisticAge, setOptimisticAge] = useOptimistic(age);
const [optimisticName, setOptimisticName] = useOptimistic(name);
const [optimisticTodos, setOptimisticTodos] = useOptimistic(todos, reducer);
// ...useOptimistic 返回一个恰好包含两个元素的数组:
- 最初设置为提供的值的乐观状态。
- 允许你在Action期间临时更改状态的 set函数 。
- 如果提供了 reducer ,它将在返回乐观状态之前运行。
要使用 乐观状态,请在 Action 内调用 set 函数。
操作是在 startTransition 内调用的函数:
🌐 Actions are functions called inside startTransition:
function onAgeChange(e) {
startTransition(async () => {
setOptimisticAge(42);
const newAge = await postAge(42);
setAge(newAge);
});
}React 会先渲染乐观状态 42,同时 age 保持当前值。Action 会等待 POST,然后为 age 和 optimisticAge 渲染 newAge。
🌐 React will render the optimistic state 42 first while the age remains the current age. The Action waits for POST, and then renders the newAge for both age and optimisticAge.
查看 乐观状态如何运作 以深入了解。
🌐 See How optimistic state works for a deep dive.
在 Action 属性中使用乐观状态
🌐 Using optimistic state in Action props
在一个 Action prop 中,你可以直接调用乐观设置器,而无需 startTransition。
🌐 In an Action prop, you can call the optimistic setter directly without startTransition.
此示例在 <form> submitAction 属性中设置乐观状态:
🌐 This example sets optimistic state inside a <form> submitAction prop:
import { useOptimistic, startTransition } from 'react'; import { updateName } from './actions.js'; export default function EditName({ name, action }) { const [optimisticName, setOptimisticName] = useOptimistic(name); async function submitAction(formData) { const newName = formData.get('name'); setOptimisticName(newName); const updatedName = await updateName(newName); startTransition(() => { action(updatedName); }) } return ( <form action={submitAction}> <p>Your name is: {optimisticName}</p> <p> <label>Change it: </label> <input type="text" name="name" disabled={name !== optimisticName} /> </p> </form> ); }
在此示例中,当用户提交表单时,optimisticName 会立即更新以乐观地显示 newName,同时服务器请求正在进行中。当请求完成时,name 和 optimisticName 会使用响应中的实际 updatedName 渲染。
🌐 In this example, when the user submits the form, the optimisticName updates immediately to show the newName optimistically while the server request is in progress. When the request completes, name and optimisticName are rendered with the actual updatedName from the response.
深入研究
🌐 Why doesn’t this need startTransition?
按照惯例,在 startTransition 中调用的 props 被命名为 “Action”。
🌐 By convention, props called inside startTransition are named with “Action”.
由于 submitAction 的命名包含 “Action”,你知道它已经在 startTransition 中被调用。
🌐 Since submitAction is named with “Action”, you know it’s already called inside startTransition.
请参阅 从组件中暴露 action 属性 了解 Action 属性模式。
🌐 See Exposing action prop from components for the Action prop pattern.
将乐观状态添加到操作属性
🌐 Adding optimistic state to Action props
在创建 Action prop 时,你可以添加 useOptimistic 来显示即时反馈。
🌐 When creating an Action prop, you can add useOptimistic to show immediate feedback.
这是一个按钮,在 action 待处理时会显示“提交中…”:
🌐 Here’s a button that shows “Submitting…” while the action is pending:
import { useOptimistic, startTransition } from 'react'; export default function Button({ action, children }) { const [isPending, setIsPending] = useOptimistic(false); return ( <button disabled={isPending} onClick={() => { startTransition(async () => { setIsPending(true); await action(); }); }} > {isPending ? 'Submitting...' : children} </button> ); }
当按钮被点击时,setIsPending(true) 使用乐观状态立即显示“正在提交…”,并禁用按钮。当操作完成后,isPending 会自动渲染为 false。
🌐 When the button is clicked, setIsPending(true) uses optimistic state to immediately show “Submitting…” and disable the button. When the Action is done, isPending is rendered as false automatically.
此模式会自动显示待处理状态,但 action 属性与 Button 一起使用时:
🌐 This pattern automatically shows a pending state however action prop is used with Button:
// Show pending state for a state update
<Button action={() => { setState(c => c + 1) }} />
// Show pending state for a navigation
<Button action={() => { navigate('/done') }} />
// Show pending state for a POST
<Button action={async () => { await fetch(/* ... */) }} />
// Show pending state for any combination
<Button action={async () => {
setState(c => c + 1);
await fetch(/* ... */);
navigate('/done');
}} />待处理状态将显示,直到 action 属性中的所有内容完成为止。
🌐 The pending state will be shown until everything in the action prop is finished.
乐观地更新 props 或 state
🌐 Updating props or state optimistically
你可以将 props 或 state 封装在 useOptimistic 中,以在操作进行时立即更新它。
🌐 You can wrap props or state in useOptimistic to update it immediately while an Action is in progress.
在这个例子中,LikeButton 接收 isLiked 作为一个 prop,并在点击时立即切换它:
🌐 In this example, LikeButton receives isLiked as a prop and immediately toggles it when clicked:
import { useState, useOptimistic, startTransition } from 'react'; import { toggleLike } from './actions.js'; export default function App() { const [isLiked, setIsLiked] = useState(false); const [optimisticIsLiked, setOptimisticIsLiked] = useOptimistic(isLiked); function handleClick() { startTransition(async () => { const newValue = !optimisticIsLiked console.log('⏳ setting optimistic state: ' + newValue); setOptimisticIsLiked(newValue); const updatedValue = await toggleLike(newValue); startTransition(() => { console.log('⏳ setting real state: ' + updatedValue ); setIsLiked(updatedValue); }); }); } if (optimisticIsLiked !== isLiked) { console.log('✅ rendering optimistic state: ' + optimisticIsLiked); } else { console.log('✅ rendering real value: ' + optimisticIsLiked); } return ( <button onClick={handleClick}> {optimisticIsLiked ? '❤️ Unlike' : '🤍 Like'} </button> ); }
当按钮被点击时,setOptimisticIsLiked 会立即更新显示状态以显示心形为已点赞。同时,await toggleLike 在后台运行。当 await 完成时,setIsLiked 的父组件会更新“真实”的 isLiked 状态,并且乐观状态会被渲染以匹配这个新值。
🌐 When the button is clicked, setOptimisticIsLiked immediately updates the displayed state to show the heart as liked. Meanwhile, await toggleLike runs in the background. When the await completes, setIsLiked parent updates the “real” isLiked state, and the optimistic state is rendered to match this new value.
一起更新多个值
🌐 Updating multiple values together
当乐观更新影响多个相关值时,使用 reducer 一起更新它们。这可以确保 UI 保持一致。
🌐 When an optimistic update affects multiple related values, use a reducer to update them together. This ensures the UI stays consistent.
这里有一个关注按钮,可以同时更新关注状态和粉丝数量:
🌐 Here’s a follow button that updates both the follow state and follower count:
import { useOptimistic, startTransition } from 'react'; export default function FollowButton({ user, followAction }) { const [optimisticState, updateOptimistic] = useOptimistic( { isFollowing: user.isFollowing, followerCount: user.followerCount }, (current, isFollowing) => ({ isFollowing, followerCount: current.followerCount + (isFollowing ? 1 : -1) }) ); function handleClick() { const newFollowState = !optimisticState.isFollowing; startTransition(async () => { updateOptimistic(newFollowState); await followAction(newFollowState); }); } return ( <div> <p><strong>{user.name}</strong></p> <p>{optimisticState.followerCount} followers</p> <button onClick={handleClick}> {optimisticState.isFollowing ? 'Unfollow' : 'Follow'} </button> </div> ); }
reducer 接收到新的 isFollowing 值,并在一次更新中计算新的关注状态和更新后的粉丝数量。这确保按钮文本和数量始终保持同步。
🌐 The reducer receives the new isFollowing value and calculates both the new follow state and the updated follower count in a single update. This ensures the button text and count always stay in sync.
深入研究
🌐 Choosing between updaters and reducers
useOptimistic 支持基于当前状态计算状态的两种模式:
更新器函数 的工作方式类似 useState 更新器。将一个函数传递给设置器:
const [optimistic, setOptimistic] = useOptimistic(value);
setOptimistic(current => !current);Reducers 将更新逻辑与 setter 调用分开:
const [optimistic, dispatch] = useOptimistic(value, (current, action) => {
// Calculate next state based on current and action
});
dispatch(action);使用更新器 进行计算,在这种情况下,setter 调用自然地描述了更新。这类似于将 setState(prev => ...) 与 useState 一起使用。
使用 reducers 当你需要传递数据到更新(例如要添加哪个项目)或在使用单个钩子处理多种类型的更新时。
为什么使用 reducer?
当基础状态在你的过渡(Transition)仍在处理中时可能会发生变化时,Reducers 是必不可少的。如果在你的添加操作待处理时 todos 发生变化(例如,另一个用户添加了一个待办事项),React 会使用新的 todos 重新运行你的 reducer 来重新计算显示内容。这确保了你的新待办事项会被添加到最新的列表中,而不是过时的副本。
🌐 Reducers are essential when the base state might change while your Transition is pending. If todos changes while your add is pending (for example, another user added a todo), React will re-run your reducer with the new todos to recalculate what to show. This ensures your new todo is added to the latest list, not an outdated copy.
像 setOptimistic(prev => [...prev, newItem]) 这样的更新函数只能看到从过渡开始时的状态,而无法获取异步操作期间发生的任何更新。
🌐 An updater function like setOptimistic(prev => [...prev, newItem]) would only see the state from when the Transition started, missing any updates that happened during the async work.
乐观地添加到列表中
🌐 Optimistically adding to a list
当你需要乐观地向列表中添加项目时,使用 reducer:
🌐 When you need to optimistically add items to a list, use a reducer:
import { useOptimistic, startTransition } from 'react'; export default function TodoList({ todos, addTodoAction }) { const [optimisticTodos, addOptimisticTodo] = useOptimistic( todos, (currentTodos, newTodo) => [ ...currentTodos, { id: newTodo.id, text: newTodo.text, pending: true } ] ); function handleAddTodo(text) { const newTodo = { id: crypto.randomUUID(), text: text }; startTransition(async () => { addOptimisticTodo(newTodo); await addTodoAction(newTodo); }); } return ( <div> <button onClick={() => handleAddTodo('New todo')}>Add Todo</button> <ul> {optimisticTodos.map(todo => ( <li key={todo.id}> {todo.text} {todo.pending && "(Adding...)"} </li> ))} </ul> </div> ); }
reducer 接收当前的待办事项列表和要添加的新待办事项。这一点很重要,因为如果在你的添加操作尚未完成时 todos 属性发生变化(例如,另一个用户添加了待办事项),React 会通过使用更新后的列表重新运行 reducer 来更新你的乐观状态。这确保你的新待办事项被添加到最新的列表中,而不是旧的副本。
🌐 The reducer receives the current list of todos and the new todo to add. This is important because if the todos prop changes while your add is pending (for example, another user added a todo), React will update your optimistic state by re-running the reducer with the updated list. This ensures your new todo is added to the latest list, not an outdated copy.
处理多种 action 类型
🌐 Handling multiple action types
当你需要处理多种类型的乐观更新(例如添加和删除项目)时,使用带有 action 对象的 reducer 模式。
🌐 When you need to handle multiple types of optimistic updates (like adding and removing items), use a reducer pattern with action objects.
这个购物车示例展示了如何使用单个 reducer 处理添加和移除操作:
🌐 This shopping cart example shows how to handle add and remove with a single reducer:
import { useOptimistic, startTransition } from 'react'; export default function ShoppingCart({ cart, cartActions }) { const [optimisticCart, dispatch] = useOptimistic( cart, (currentCart, action) => { switch (action.type) { case 'add': const exists = currentCart.find(item => item.id === action.item.id); if (exists) { return currentCart.map(item => item.id === action.item.id ? { ...item, quantity: item.quantity + 1, pending: true } : item ); } return [...currentCart, { ...action.item, quantity: 1, pending: true }]; case 'remove': return currentCart.filter(item => item.id !== action.id); case 'update_quantity': return currentCart.map(item => item.id === action.id ? { ...item, quantity: action.quantity, pending: true } : item ); default: return currentCart; } } ); function handleAdd(item) { startTransition(async () => { dispatch({ type: 'add', item }); await cartActions.add(item); }); } function handleRemove(id) { startTransition(async () => { dispatch({ type: 'remove', id }); await cartActions.remove(id); }); } function handleUpdateQuantity(id, quantity) { startTransition(async () => { dispatch({ type: 'update_quantity', id, quantity }); await cartActions.updateQuantity(id, quantity); }); } const total = optimisticCart.reduce( (sum, item) => sum + item.price * item.quantity, 0 ); return ( <div> <h2>Shopping Cart</h2> <div style={{ marginBottom: 16 }}> <button onClick={() => handleAdd({ id: 1, name: 'T-Shirt', price: 25 })}> Add T-Shirt ($25) </button>{' '} <button onClick={() => handleAdd({ id: 2, name: 'Mug', price: 15 })}> Add Mug ($15) </button> </div> {optimisticCart.length === 0 ? ( <p>Your cart is empty</p> ) : ( <ul> {optimisticCart.map(item => ( <li key={item.id}> {item.name} - ${item.price} × {item.quantity} {' '}= ${item.price * item.quantity} <button onClick={() => handleRemove(item.id)} style={{ marginLeft: 8 }} > Remove </button> {item.pending && ' ...'} </li> ))} </ul> )} <p><strong>Total: ${total}</strong></p> </div> ); }
该 reducer 处理三种 action 类型(add、remove、update_quantity),并返回每种类型的新乐观状态。每个 action 设置一个 pending: true 标志,这样你就可以在 Server Function 运行时显示视觉反馈。
🌐 The reducer handles three action types (add, remove, update_quantity) and returns the new optimistic state for each. Each action sets a pending: true flag so you can show visual feedback while the Server Function runs.
带错误恢复的乐观删除
🌐 Optimistic delete with error recovery
在乐观删除项目时,你应该处理操作失败的情况。
🌐 When deleting items optimistically, you should handle the case where the Action fails.
此示例演示了在删除失败时如何显示错误消息,以及用户界面如何自动回滚以再次显示该项目。
🌐 This example shows how to display an error message when a delete fails, and the UI automatically rolls back to show the item again.
import { useState, useOptimistic, startTransition } from 'react'; export default function ItemList({ items, deleteAction }) { const [error, setError] = useState(null); const [optimisticItems, removeItem] = useOptimistic( items, (currentItems, idToRemove) => currentItems.map(item => item.id === idToRemove ? { ...item, deleting: true } : item ) ); function handleDelete(id) { setError(null); startTransition(async () => { removeItem(id); try { await deleteAction(id); } catch (e) { setError(e.message); } }); } return ( <div> <h2>Your Items</h2> <ul> {optimisticItems.map(item => ( <li key={item.id} style={{ opacity: item.deleting ? 0.5 : 1, textDecoration: item.deleting ? 'line-through' : 'none', transition: 'opacity 0.2s' }} > {item.name} <button onClick={() => handleDelete(item.id)} disabled={item.deleting} style={{ marginLeft: 8 }} > {item.deleting ? 'Deleting...' : 'Delete'} </button> </li> ))} </ul> {error && ( <p style={{ color: 'red', padding: 8, background: '#fee' }}> {error} </p> )} </div> ); }
尝试删除“部署到生产”。当删除失败时,该项目会自动重新出现在列表中。
🌐 Try deleting ‘Deploy to production’. When the delete fails, the item automatically reappears in the list.
故障排除
🌐 Troubleshooting
我收到一个错误:“乐观状态更新发生在 Transition 或 Action 之外”
🌐 I’m getting an error: “An optimistic state update occurred outside a Transition or Action”
你可能会看到此错误:
🌐 You may see this error:
startTransition.乐观设置函数必须在 startTransition 内调用:
🌐 The optimistic setter function must be called inside startTransition:
// 🚩 Incorrect: outside a Transition
function handleClick() {
setOptimistic(newValue); // Warning!
// ...
}
// ✅ Correct: inside a Transition
function handleClick() {
startTransition(async () => {
setOptimistic(newValue);
// ...
});
}
// ✅ Also correct: inside an Action prop
function submitAction(formData) {
setOptimistic(newValue);
// ...
}当你在 Action 外部调用 setter 时,乐观状态会短暂出现,然后立即恢复到原始值。这是因为在你的 Action 运行时,没有 Transition 来“保持”乐观状态。
🌐 When you call the setter outside an Action, the optimistic state will briefly appear and then immediately revert back to the original value. This happens because there’s no Transition to “hold” the optimistic state while your Action runs.
我收到一个错误:“在渲染时无法更新乐观状态”
🌐 I’m getting an error: “Cannot update optimistic state while rendering”
你可能会看到此错误:
🌐 You may see this error:
当你在组件的渲染阶段调用乐观设置器时,会发生此错误。你只能在事件处理程序、效果或其他回调中调用它:
🌐 This error occurs when you call the optimistic setter during the render phase of a component. You can only call it from event handlers, effects, or other callbacks:
// 🚩 Incorrect: calling during render
function MyComponent({ items }) {
const [isPending, setPending] = useOptimistic(false);
// This runs during render - not allowed!
setPending(true);
// ...
}
// ✅ Correct: calling inside startTransition
function MyComponent({ items }) {
const [isPending, setPending] = useOptimistic(false);
function handleClick() {
startTransition(() => {
setPending(true);
// ...
});
}
// ...
}
// ✅ Also correct: calling from an Action
function MyComponent({ items }) {
const [isPending, setPending] = useOptimistic(false);
function action() {
setPending(true);
// ...
}
// ...
}我的乐观更新显示过时的值
🌐 My optimistic updates show stale values
如果你的乐观状态似乎基于旧数据,考虑使用更新函数或 reducer 来根据当前状态计算乐观状态。
🌐 If your optimistic state seems to be based on old data, consider using an updater function or reducer to calculate the optimistic state relative to the current state.
// May show stale data if state changes during Action
const [optimistic, setOptimistic] = useOptimistic(count);
setOptimistic(5); // Always sets to 5, even if count changed
// Better: relative updates handle state changes correctly
const [optimistic, adjust] = useOptimistic(count, (current, delta) => current + delta);
adjust(1); // Always adds 1 to whatever the current count is详情请参见 根据当前状态更新状态。
🌐 See Updating state based on the current state for details.
我不知道我的乐观更新是否正在等待处理中
🌐 I don’t know if my optimistic update is pending
要知道 useOptimistic 何时挂起,你有三个选项:
🌐 To know when useOptimistic is pending, you have three options:
- 检查
optimisticValue === value
const [optimistic, setOptimistic] = useOptimistic(value);
const isPending = optimistic !== value;如果值不相等,则正在进行转换。
🌐 If the values are not equal, there’s a Transition in progress.
- 添加一个
useTransition
const [isPending, startTransition] = useTransition();
const [optimistic, setOptimistic] = useOptimistic(value);
//...
startTransition(() => {
setOptimistic(state);
})由于 useTransition 在底层使用 useOptimistic 来处理 isPending,这等同于选项 1。
🌐 Since useTransition uses useOptimistic for isPending under the hood, this is equivalent to option 1.
- 在你的 reducer 中添加一个
pending标志
const [optimistic, addOptimistic] = useOptimistic(
items,
(state, newItem) => [...state, { ...newItem, isPending: true }]
);由于每个乐观项都有自己的标志,你可以为单个项显示加载状态。
🌐 Since each optimistic item has its own flag, you can show loading state for individual items.