Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add async transitions to React 19 docs #6837

Draft
wants to merge 1 commit into
base: v19
Choose a base branch
from

Conversation

rickhanlonii
Copy link
Member

@rickhanlonii rickhanlonii commented May 5, 2024

Preview

This isn't final, I'm still iterating on the best way to explain this so please resist the urge to share as if it's the actual explanation

Overview

This PR updates the useTransition docs to include async transitions.

  • Reworks existing content that says it's only sync
  • Adds example of creating an action
  • Adds troubleshooting for set state after await
  • Adds troubleshooting for out-of-order requests

Copy link

vercel bot commented May 5, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
19-react-dev ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 6, 2024 3:00pm
react-dev ✅ Ready (Inspect) Visit Preview May 6, 2024 3:00pm

Copy link

vercel bot commented May 5, 2024

@rickhanlonii is attempting to deploy a commit to the Meta Open Source Team on Vercel.

A member of the Team first needs to authorize it.

#### Parameters {/*starttransition-parameters*/}

* `scope`: A function that updates some state by calling one or more [`set` functions.](/reference/react/useState#setstate) React immediately calls `scope` with no parameters and marks all state updates scheduled synchronously during the `scope` function call as Transitions. They will be [non-blocking](#marking-a-state-update-as-a-non-blocking-transition) and [will not display unwanted loading indicators.](#preventing-unwanted-loading-indicators)
* `scope`: A function that updates some state by calling one or more [`set` functions](/reference/react/useState#setstate). React immediately calls `scope` with no parameters and marks all state updates scheduled synchronously during the `scope` function call as Transitions. Any async calls awaited in the `scope` will be included in the transition, but currently require wrapping any `set` functions after the request in an additional `startTransition` (see [Troubleshooting](#react-doesnt-treat-my-state-update-after-await-as-a-transition)). State updates marked as Transitions will be [non-blocking](#marking-a-state-update-as-a-non-blocking-transition) and [will not display unwanted loading indicators.](#preventing-unwanted-loading-indicators).
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
* `scope`: A function that updates some state by calling one or more [`set` functions](/reference/react/useState#setstate). React immediately calls `scope` with no parameters and marks all state updates scheduled synchronously during the `scope` function call as Transitions. Any async calls awaited in the `scope` will be included in the transition, but currently require wrapping any `set` functions after the request in an additional `startTransition` (see [Troubleshooting](#react-doesnt-treat-my-state-update-after-await-as-a-transition)). State updates marked as Transitions will be [non-blocking](#marking-a-state-update-as-a-non-blocking-transition) and [will not display unwanted loading indicators.](#preventing-unwanted-loading-indicators).
* `scope`: A function that updates some State by calling one or more [`set` functions](/reference/react/useState#setstate). React immediately calls `scope` with no parameters and marks all state updates scheduled synchronously during the `scope` function call as Transitions. Any async calls awaited in the `scope` will be included in the transition, but currently require wrapping any `set` functions after the request in an additional `startTransition` (see [Troubleshooting](#react-doesnt-treat-my-state-update-after-await-as-a-transition)). State updates marked as Transitions will be [non-blocking](#marking-a-state-update-as-a-non-blocking-transition) and [will not display unwanted loading indicators.](#preventing-unwanted-loading-indicators).

Copy link
Collaborator

Choose a reason for hiding this comment

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

I see a lot of state references from react to be in smallcase? Is this intentional?

@rickhanlonii rickhanlonii changed the title Add async transitions to React 19 docs [WIP] Add async transitions to React 19 docs May 6, 2024
Comment on lines +455 to +457
startTransition(() => {
setQuantity(savedQuantity);
});
Copy link
Contributor

@slorber slorber May 7, 2024

Choose a reason for hiding this comment

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

It's probably useful to add a small note in this sandbox that explains why we need the extra startTransition here. I was looking for this info but it only appears later in the troubleshooting section.

I also tried to remove the extra startTransition() to see if I got a bug, but in this case I didn't really see any behavior change. It's probably only due to that specific example since setQuantity happens last

@tom-sherman
Copy link
Contributor

To be honest I really don't see async transitions (with useTransition) being practically adoptable without the race condition handling built in. Without this, they are simply a target for libraries as the queue handling is very tricky.

There are so many footguns with using them on their own that I think there needs to be some massive disclaimer, and for them to be called out as a tool for library and framework authors.

@rickhanlonii
Copy link
Member Author

rickhanlonii commented May 7, 2024

@tom-sherman it's going to be more common for app developers to use useActionState or use components with an action prop like <form> to create an Action, instead of using raw transitions directly. Using async transitions directly is targeted to library developers who intend to write their own queuing logic or component library authors to expose an action prop.

@rickhanlonii
Copy link
Member Author

It's similar to how doing event handlers to submit data on your own is tricky (for example, this case isn't handled in a raw event handler either), and libraries make that easier using raw events and exposing props to limit the complexity to app developers.

@rickhanlonii rickhanlonii marked this pull request as draft May 7, 2024 19:46
@rickhanlonii
Copy link
Member Author

Converting back to WIP, I think there is a better way to explain this.

Comment on lines +2141 to +2146
startTransition(async () => {
await someAsyncFunction();
// ✅ Using startTransition *after* await
startTransition(() => {
setPage('/about');
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Something that isn't immediately clear to me:

Do we need this nested startTransition for:

  • regular form actions too?
  • calling setOptimisticValue?

I assume we do, but not 100% sure since I don't see any example in the docs.

The <form> actions examples are likely to use useActionState instead of doing startTransition(() => setState())

The useOptimistic doc only calls the setter as the first step, probably the most common case, but maybe it can make sense in some rare cases to call it after await? I don't know 🤷‍♂️

  async function formAction(formData) {
    addOptimisticMessage(formData.get("message"));
    formRef.current.reset();
    await sendMessage(formData);
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants