mirror of
https://github.com/reactjs/react.dev.git
synced 2026-02-22 03:42:14 +00:00
fix action pattern in useTransition / useOptimistic (#7796)
* fix action pattern in useOptimistic * update useTransition too
This commit is contained in:
@@ -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} />;
|
||||
|
||||
@@ -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 (
|
||||
|
||||
Reference in New Issue
Block a user