fix action pattern in useTransition / useOptimistic (#7796)

* fix action pattern in useOptimistic

* update useTransition too
This commit is contained in:
Ricky
2025-04-30 10:18:55 -04:00
committed by GitHub
parent d6c4c0fee5
commit a3e9466dfe
2 changed files with 39 additions and 24 deletions

View File

@@ -74,7 +74,9 @@ function Thread({ messages, sendMessageAction }) {
function formAction(formData) {
addOptimisticMessage(formData.get("message"));
formRef.current.reset();
sendMessageAction(formData);
startTransition(async () => {
await sendMessageAction(formData);
});
}
const [optimisticMessages, addOptimisticMessage] = useOptimistic(
messages,
@@ -108,12 +110,10 @@ export default function App() {
const [messages, setMessages] = useState([
{ text: "Hello there!", sending: false, key: 1 }
]);
function sendMessageAction(formData) {
startTransition(async () => {
const sentMessage = await deliverMessage(formData.get("message"));
startTransition(() => {
setMessages((messages) => [{ text: sentMessage }, ...messages]);
})
async function sendMessageAction(formData) {
const sentMessage = await deliverMessage(formData.get("message"));
startTransition(() => {
setMessages((messages) => [{ text: sentMessage }, ...messages]);
})
}
return <Thread messages={messages} sendMessageAction={sendMessageAction} />;

View File

@@ -77,8 +77,8 @@ function SubmitButton({ submitAction }) {
<button
disabled={isPending}
onClick={() => {
startTransition(() => {
submitAction();
startTransition(async () => {
await submitAction();
});
}}
>
@@ -227,9 +227,9 @@ import { startTransition } from "react";
export default function Item({action}) {
function handleChange(event) {
// To expose an action prop, call the callback in startTransition.
// To expose an action prop, await the callback in startTransition.
startTransition(async () => {
action(event.target.value);
await action(event.target.value);
})
}
return (
@@ -585,10 +585,9 @@ This solution makes the app feel slow, because the user must wait each time they
You can expose an `action` prop from a component to allow a parent to call an Action.
For example, this `TabButton` component wraps its `onClick` logic in an `action` prop:
```js {8-10}
```js {8-12}
export default function TabButton({ action, children, isActive }) {
const [isPending, startTransition] = useTransition();
if (isActive) {
@@ -596,8 +595,10 @@ export default function TabButton({ action, children, isActive }) {
}
return (
<button onClick={() => {
startTransition(() => {
action();
startTransition(async () => {
// await the action that's passed in.
// This allows it to be either sync or async.
await action();
});
}}>
{children}
@@ -656,10 +657,15 @@ export default function TabButton({ action, children, isActive }) {
if (isActive) {
return <b>{children}</b>
}
if (isPending) {
return <b className="pending">{children}</b>;
}
return (
<button onClick={() => {
startTransition(() => {
action();
<button onClick={async () => {
startTransition(async () => {
// await the action that's passed in.
// This allows it to be either sync or async.
await action();
});
}}>
{children}
@@ -729,10 +735,19 @@ export default function ContactTab() {
```css
button { margin-right: 10px }
b { display: inline-block; margin-right: 10px; }
.pending { color: #777; }
```
</Sandpack>
<Note>
When exposing an `action` prop from a component, you should `await` it inside the transition.
This allows the `action` callback to be either synchronous or asynchronous without requiring an additional `startTransition` to wrap the `await` in the action.
</Note>
---
### Displaying a pending visual state {/*displaying-a-pending-visual-state*/}
@@ -804,8 +819,8 @@ export default function TabButton({ action, children, isActive }) {
}
return (
<button onClick={() => {
startTransition(() => {
action();
startTransition(async () => {
await action();
});
}}>
{children}
@@ -1095,8 +1110,8 @@ export default function TabButton({ action, children, isActive }) {
}
return (
<button onClick={() => {
startTransition(() => {
action();
startTransition(async () => {
await action();
});
}}>
{children}
@@ -1822,8 +1837,8 @@ import {startTransition} from 'react';
export default function Item({action}) {
function handleChange(e) {
// Update the quantity in an Action.
startTransition(() => {
action(e.target.value);
startTransition(async () => {
await action(e.target.value);
});
}
return (