Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions src/content/reference/react/useOptimistic.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ function AppContainer() {
* `optimisticState`: The resulting optimistic state. It is equal to `state` unless an action is pending, in which case it is equal to the value returned by `updateFn`.
* `addOptimistic`: `addOptimistic` is the dispatching function to call when you have an optimistic update. It takes one argument, `optimisticValue`, of any type and will call the `updateFn` with `state` and `optimisticValue`.


#### Caveats {/*caveats*/}

* An optimistic state update needs to be called in an action or wrapped with `startTransition`. If it's used outside an action or a transition React will throw an error.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* An optimistic state update needs to be called in an action or wrapped with `startTransition`. If it's used outside an action or a transition React will throw an error.
* An optimistic state update needs to be called in an action or wrapped with `startTransition`. If it's used outside an action or a transition, React will throw an error.


---

## Usage {/*usage*/}
Expand Down Expand Up @@ -143,3 +148,67 @@ export async function deliverMessage(message) {
```

</Sandpack>

---

### Optimistically update the UI while waiting for a network request {/*optimistically-update-while-waiting-for-network*/}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I may be forgetting something but what was the reasoning for us to add this example? It seems quite similar to the form action example above, the only difference being that it isn't in a form? I think we'd want to add some description here of why this example is worth highlighting.

In addition, I think it'd be valuable to expand the example and show a failed optimistic update.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it is similar but wanted to showcase another example that isn't using a form. I agree on adding in a failure example and have it as a followup

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a line about that?
Something like

Suggested change
### Optimistically update the UI while waiting for a network request {/*optimistically-update-while-waiting-for-network*/}
### Optimistically update the UI while waiting for a network request {/*optimistically-update-while-waiting-for-
network*/}
`useOptimistic` can also be used outside of forms, for any network request but within a transition.


<Sandpack>

```js like-button.js active
import { startTransition, useOptimistic } from "react";

export function LikeButton({ likes, updateLikes, disabled }) {
const [optimisticLikes, addOptimisticLike] = useOptimistic(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also the example for me is not updating the like count from 0->1

CleanShot 2023-12-15 at 09 45 56

re: https://react-ac6nnqh06-fbopensource.vercel.app/reference/react/useOptimistic#noun-labs-1201738-(2)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lunaleaps One way to fix this example would be to start a transition with useTransition hook instead of using the standalone startTransition function. This way you'll be able to:

  • replace the disabled prop with the transition's pending state,
  • make the arrow function (scope) passed to startTransition async and await updateLikes inside (crucial).

It fixes the issue while slightly simplifying the code, here's the preview:
https://codesandbox.io/p/sandbox/gallant-nova-8w3s2h

likes,
(likes) => likes + 1
);

async function addLike() {
startTransition(() => {
addOptimisticLike();
updateLikes(likes);
})
}

return (
<button disabled={disabled} onClick={addLike}>
Like {optimisticLikes}
</button>
);
}
```


```js App.js
import { useState } from "react";
import { LikeButton } from "./like-button.js";

export default function App() {
const [likes, setLikes] = useState(0);
const [updating, setUpdating] = useState(false);
async function updateLikes(num) {
setUpdating(true);
await new Promise((res) => setTimeout(res, 1000));
setLikes(num + 1);
setUpdating(false);
}
return (
<LikeButton likes={likes} updateLikes={updateLikes} disabled={updating} />
);
}
```

```json package.json hidden
{
"dependencies": {
"react": "experimental",
"react-dom": "experimental",
"react-scripts": "^5.0.0"
},
"main": "/index.js",
"devDependencies": {}
}
```

</Sandpack>