Skip to content

Commit 0f71d9d

Browse files
committed
Updates from review
1 parent 76b8be1 commit 0f71d9d

File tree

1 file changed

+93
-78
lines changed

1 file changed

+93
-78
lines changed

src/content/reference/react/useOptimistic.md

Lines changed: 93 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,16 @@ If the Action throws an error, the Transition still ends, and React renders with
103103
104104
### `set` functions, like `setOptimistic(optimisticValue)` {/*setoptimistic*/}
105105
106-
The `set` function returned by `useOptimistic` lets you update the state to a different value inside an Action or Transition and trigger an immediate re-render. You can pass the next state directly, or a function that calculates it from the previous state:
106+
The `set` function returned by `useOptimistic` lets you update the state for the duration of a Transition. You can pass the next state directly, or a function that calculates it from the previous state:
107107
108108
```js
109-
const [optimisticLike, setOptimisticLike] = useOptimistic(name);
109+
const [optimisticLike, setOptimisticLike] = useOptimistic(false);
110+
const [optimisticSubs, setOptimisticSubs] = useOptimistic(subs);
110111

111112
function handleClick() {
112113
startTransition(async () => {
113114
setOptimisticLike(true);
114-
setOptimisticLike(liked => !liked);
115+
setOptimisticSubs(a => a + 1);
115116
await saveChanges();
116117
});
117118
}
@@ -128,15 +129,15 @@ function handleClick() {
128129
129130
#### Caveats {/*setoptimistic-caveats*/}
130131
131-
* The `set` function must be called inside a [Transition](/reference/react/useTransition) using [`startTransition`](/reference/react/startTransition) or inside an [Action](/reference/react/useTransition#perform-non-blocking-updates-with-actions). If you call the setter outside an Action or Transition, the optimistic value will briefly appear and then immediately revert.
132+
* The `set` function must be called inside a [Transition](/reference/react/useTransition) using [`startTransition`](/reference/react/startTransition) or inside an [Action](/reference/react/useTransition#perform-non-blocking-updates-with-actions). If you call the setter outside an Action or Transition, [React will show a warning](#an-optimistic-state-update-occurred-outside-a-transition-or-action) and the optimistic value will briefly render.
132133
133134
---
134135
135136
## Usage {/*usage*/}
136137
137138
### Adding optimistic state to a component {/*adding-optimistic-state-to-a-component*/}
138139
139-
Call useOptimistic at the top level of your component to declare one or more optimistic states.
140+
Call `useOptimistic` at the top level of your component to declare one or more optimistic states.
140141
141142
```js [[1, 4, "age"], [1, 5, "name"], [1, 6, "todos"], [2, 4, "optimisticAge"], [2, 5, "optimisticName"], [2, 6, "optimisticTodos"], [3, 4, "setOptimisticAge"], [3, 5, "setOptimisticName"], [3, 6, "setOptimisticTodos"], [4, 6, "reducer"]]
142143
import { useOptimistic } from 'react';
@@ -152,7 +153,7 @@ function MyComponent({age, name, todos}) {
152153
153154
1. The <CodeStep step={2}>current state</CodeStep> of the optimistic value, returning the <CodeStep step={1}>state</CodeStep> provided.
154155
2. The <CodeStep step={3}>set function</CodeStep> that lets you temporarily change the state during an Action or Transition.
155-
* If a <CodeStep step={1}>reducer</CodeStep> is provided, it will run before rendering the temporary state.
156+
* If a <CodeStep step={4}>reducer</CodeStep> is provided, it will run before rendering the temporary state.
156157
157158
To use the optimistic state, call the set function inside a Transition:
158159
@@ -165,17 +166,99 @@ function handleClick(e) {
165166
}
166167
```
167168
168-
Or call it inside an action:
169+
React will render the optimistic state first, then complete the Transition with the new value.
169170
170-
```js [[3, 3, "setOptimisticName"]]
171+
<Note>
172+
173+
When using [Action props](/reference/react/useTransition#exposing-action-props-from-components), you can call the set function without `startTransition`:
174+
175+
```js [[3, 2, "setOptimisticName"]]
171176
async function submitAction() {
172-
// By convention, Actions are called inside startTransition.
173177
setOptimisticName('Taylor');
174178
await updateName('Taylor');
175179
}
176180
```
177181
178-
React will render the optimistic state first, then complete the Action or Transition with the new value.
182+
This works because Action props are already called inside a Transition.
183+
184+
For an example, see: [Using optimistic state in Action props](#using-optimistic-state-in-action-props).
185+
186+
</Note>
187+
188+
### Using optimistic state in Action props {/*using-optimistic-state-in-action-props*/}
189+
190+
In an [Action prop](/reference/react/useTransition#exposing-action-props-from-components), you can call the optimistic setter directly without `startTransition`.
191+
192+
This example sets optimistc state inside a `<form>` `submitAction` prop:
193+
194+
<Sandpack>
195+
196+
```js src/App.js
197+
import { useState, startTransition } from 'react';
198+
import EditName from './EditName';
199+
200+
export default function App() {
201+
const [name, setName] = useState('Alice');
202+
203+
function onSubmit(newName) {
204+
startTransition(() => {
205+
setName(newName)
206+
});
207+
}
208+
return <EditName name={name} onSubmit={onSubmit} />;
209+
}
210+
```
211+
212+
```js src/EditName.js active
213+
import { useOptimistic } from 'react';
214+
import { updateName } from './actions.js';
215+
216+
export default function EditName({ name, onSubmit }) {
217+
const [optimisticName, setOptimisticName] = useOptimistic(name);
218+
219+
async function submitAction(formData) {
220+
const newName = formData.get('name');
221+
setOptimisticName(newName);
222+
const updatedName = await updateName(newName);
223+
onSubmit(updatedName);
224+
}
225+
226+
return (
227+
<form action={submitAction}>
228+
<p>Your name is: {optimisticName}</p>
229+
<p>
230+
<label>Change it: </label>
231+
<input
232+
type="text"
233+
name="name"
234+
disabled={name !== optimisticName}
235+
/>
236+
</p>
237+
</form>
238+
);
239+
}
240+
```
241+
242+
```js src/actions.js hidden
243+
export async function updateName(name) {
244+
await new Promise((res) => setTimeout(res, 1000));
245+
return name;
246+
}
247+
```
248+
249+
</Sandpack>
250+
251+
In this example, when the user submits the form, the `optimisticName` updates immediately to show the new value while the server request is in progress. When the request completes, the real `name` is updated and the Transition completes rendering the new name.
252+
253+
<Note>
254+
255+
#### Why doesn't this need `startTransition`? {/*why-doesnt-this-need-starttransition*/}
256+
257+
The optimistic setter must be called inside a Transition to work correctly. When you pass a function to an [Action prop](/reference/react/useTransition#exposing-action-props-from-components), by convention that function is already called inside `startTransition`.
258+
259+
Since you're already in a Transition, calling `setOptimisticName` directly is valid.
260+
261+
</Note>
179262
180263
### Adding pending state to a component {/*adding-pending-state-to-a-component*/}
181264
@@ -488,74 +571,6 @@ export async function unfollowUser(name) {
488571
489572
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.
490573
491-
### Using optimistic state in Action props {/*using-optimistic-state-in-action-props*/}
492-
493-
When you pass a function to an [Action prop](/reference/react/useTransition#exposing-action-props-from-components), you can call the optimistic setter directly without wrapping it in `startTransition`. This includes form actions:
494-
495-
<Sandpack>
496-
497-
```js src/App.js
498-
import { useState } from 'react';
499-
import EditName from './EditName';
500-
501-
export default function App() {
502-
const [name, setName] = useState('Alice');
503-
504-
return <EditName name={name} setName={setName} />;
505-
}
506-
```
507-
508-
```js src/EditName.js active
509-
import { useOptimistic } from 'react';
510-
import { updateName } from './actions.js';
511-
512-
export default function EditName({ name, setName }) {
513-
const [optimisticName, setOptimisticName] = useOptimistic(name);
514-
515-
async function submitAction(formData) {
516-
const newName = formData.get('name');
517-
setOptimisticName(newName);
518-
const updatedName = await updateName(newName);
519-
setName(updatedName);
520-
}
521-
522-
return (
523-
<form action={submitAction}>
524-
<p>Your name is: {optimisticName}</p>
525-
<p>
526-
<label>Change it: </label>
527-
<input
528-
type="text"
529-
name="name"
530-
disabled={name !== optimisticName}
531-
/>
532-
</p>
533-
</form>
534-
);
535-
}
536-
```
537-
538-
```js src/actions.js hidden
539-
export async function updateName(name) {
540-
await new Promise((res) => setTimeout(res, 1000));
541-
return name;
542-
}
543-
```
544-
545-
</Sandpack>
546-
547-
In this example, when the user submits the form, the `optimisticName` updates immediately to show the new value while the server request is in progress. When the request completes, React updates the real `name` state and the Transition ends.
548-
549-
<DeepDive>
550-
551-
#### Why doesn't this need `startTransition`? {/*why-doesnt-this-need-starttransition*/}
552-
553-
The optimistic setter must be called inside a Transition to work correctly. When you pass a function to an Action prop, React automatically calls that function inside a Transition context. This means the action is already running inside a Transition when invoked.
554-
555-
Since you're already in a Transition, calling `setOptimisticName` directly is valid. The `await` inside the async function keeps the Transition open until the server responds, at which point the optimistic state reverts to the real state.
556-
557-
</DeepDive>
558-
559574
### Optimistically adding to a list {/*optimistically-adding-to-a-list*/}
560575
561576
When you need to optimistically add items to a list, use the `reducer` parameter:

0 commit comments

Comments
 (0)