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

Initial (client-only) async actions support #26621

Merged
merged 1 commit into from
Apr 19, 2023

Conversation

acdlite
Copy link
Collaborator

@acdlite acdlite commented Apr 13, 2023

Implements initial (client-only) support for async actions behind a flag. This is an experimental feature and the design isn't completely finalized but we're getting closer. It will be layered alongside other features we're working on, so it may not feel complete when considered in isolation.

The basic description is you can pass an async function to startTransition and all the transition updates that are scheduled inside that async function will be grouped together. The isPending flag will be set to true immediately, and only set back to false once the async action has completed (as well as all the updates that it triggers).

The ideal behavior would be that all updates spawned by the async action are automatically inferred and grouped together; however, doing this properly requires the upcoming (stage 2) Async Context API, which is not yet implemented by browsers. In the meantime, we will fake this by grouping together all transition updates that occur until the async function has terminated. This can lead to overgrouping between unrelated actions, which is not wrong per se, just not ideal.

If the useTransition hook is removed from the UI before an async action has completed — for example, if the user navigates to a new page — subsequent transitions will no longer be grouped with together with that action.

Another consequence of the lack of Async Context is that if you call setState inside an action but after an await, it must be wrapped in startTransition in order to be grouped properly. If we didn't require this, then there would be no way to distinguish action updates from urgent updates caused by user input, too. This is an unfortunate footgun but we can likely detect the most common mistakes using a lint rule.

Once Async Context lands in browsers, we can start warning in dev if we detect an update that hasn't been wrapped in startTransition. Then, longer term, once the feature is ubiquitous, we can rely on it for real and allow you to call setState without the additional wrapper.

Things that are not yet implemented in this PR, but will be added as follow ups:

  • Support for non-hook form of startTransition
  • Canceling the async action scope if the useTransition hook is deleted from the UI
  • Anything related to server actions

@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Apr 13, 2023
@react-sizebot
Copy link

react-sizebot commented Apr 13, 2023

Comparing: d121c67...41dfb55

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.min.js +0.08% 164.42 kB 164.55 kB +0.22% 51.69 kB 51.80 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js +0.45% 166.86 kB 167.61 kB +0.62% 52.34 kB 52.66 kB
facebook-www/ReactDOM-prod.classic.js +0.41% 564.45 kB 566.77 kB +0.80% 99.40 kB 100.20 kB
facebook-www/ReactDOM-prod.modern.js +0.42% 548.24 kB 550.56 kB +0.83% 96.71 kB 97.50 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-experimental/react-art/cjs/react-art.production.min.js +0.79% 95.66 kB 96.42 kB +0.82% 29.40 kB 29.64 kB
facebook-www/ReactART-prod.modern.js +0.70% 331.70 kB 334.01 kB +1.43% 56.11 kB 56.91 kB
facebook-www/ReactART-prod.classic.js +0.67% 342.69 kB 345.00 kB +1.35% 58.04 kB 58.83 kB
oss-experimental/react-reconciler/cjs/react-reconciler.production.min.js +0.67% 112.49 kB 113.24 kB +0.89% 34.35 kB 34.65 kB
oss-experimental/react-reconciler/cjs/react-reconciler.profiling.min.js +0.62% 121.50 kB 122.25 kB +0.82% 36.54 kB 36.84 kB
oss-experimental/react-art/umd/react-art.production.min.js +0.56% 132.93 kB 133.68 kB +0.72% 41.54 kB 41.84 kB
oss-experimental/react-art/cjs/react-art.development.js +0.46% 802.10 kB 805.82 kB +0.75% 173.96 kB 175.26 kB
facebook-www/ReactART-dev.modern.js +0.46% 893.99 kB 898.10 kB +0.75% 189.97 kB 191.39 kB
facebook-www/ReactART-dev.classic.js +0.45% 905.19 kB 909.30 kB +0.72% 192.28 kB 193.67 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js +0.45% 166.86 kB 167.61 kB +0.62% 52.34 kB 52.66 kB
oss-experimental/react-dom/umd/react-dom.production.min.js +0.45% 166.77 kB 167.52 kB +0.53% 52.76 kB 53.04 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.production.min.js +0.44% 173.07 kB 173.83 kB +0.56% 54.66 kB 54.97 kB
oss-experimental/react-art/umd/react-art.development.js +0.43% 916.98 kB 920.91 kB +0.67% 192.97 kB 194.26 kB
oss-stable-semver/react-test-renderer/umd/react-test-renderer.development.js +0.43% 809.78 kB 813.24 kB +0.68% 170.14 kB 171.29 kB
oss-stable/react-test-renderer/umd/react-test-renderer.development.js +0.43% 809.81 kB 813.27 kB +0.68% 170.16 kB 171.32 kB
oss-experimental/react-test-renderer/umd/react-test-renderer.development.js +0.43% 810.20 kB 813.66 kB +0.68% 170.27 kB 171.42 kB
oss-experimental/react-dom/cjs/react-dom.profiling.min.js +0.43% 176.49 kB 177.24 kB +0.59% 54.75 kB 55.07 kB
oss-experimental/react-dom/umd/react-dom.profiling.min.js +0.43% 175.75 kB 176.50 kB +0.53% 55.05 kB 55.34 kB
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-dev.js +0.43% 787.85 kB 791.20 kB +0.70% 170.22 kB 171.41 kB
facebook-www/ReactDOM-prod.modern.js +0.42% 548.24 kB 550.56 kB +0.83% 96.71 kB 97.50 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js +0.42% 773.10 kB 776.37 kB +0.70% 168.37 kB 169.55 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js +0.42% 773.12 kB 776.39 kB +0.70% 168.40 kB 169.57 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js +0.42% 773.49 kB 776.76 kB +0.69% 168.48 kB 169.65 kB
facebook-www/ReactTestRenderer-dev.modern.js +0.42% 804.14 kB 807.49 kB +0.69% 173.38 kB 174.59 kB
facebook-www/ReactTestRenderer-dev.classic.js +0.42% 804.14 kB 807.50 kB +0.69% 173.38 kB 174.58 kB
oss-experimental/react-reconciler/cjs/react-reconciler.development.js +0.41% 900.93 kB 904.65 kB +0.64% 191.92 kB 193.14 kB
oss-stable-semver/react-art/cjs/react-art.development.js +0.41% 794.38 kB 797.65 kB +0.68% 172.60 kB 173.78 kB
oss-stable/react-art/cjs/react-art.development.js +0.41% 794.40 kB 797.67 kB +0.68% 172.63 kB 173.80 kB
facebook-www/ReactDOM-prod.classic.js +0.41% 564.45 kB 566.77 kB +0.80% 99.40 kB 100.20 kB
facebook-www/ReactDOMTesting-prod.modern.js +0.41% 564.78 kB 567.10 kB +0.72% 100.89 kB 101.61 kB
react-native/implementations/ReactFabric-dev.js +0.40% 852.98 kB 856.41 kB +0.66% 184.61 kB 185.82 kB
facebook-www/ReactDOMTesting-prod.classic.js +0.40% 578.92 kB 581.23 kB +0.71% 103.10 kB 103.83 kB
facebook-www/ReactDOM-profiling.modern.js +0.40% 578.68 kB 580.98 kB +0.77% 101.20 kB 101.99 kB
facebook-www/ReactDOM-profiling.classic.js +0.39% 594.96 kB 597.26 kB +0.74% 103.93 kB 104.70 kB
react-native/implementations/ReactNativeRenderer-dev.js +0.39% 868.68 kB 872.04 kB +0.63% 189.05 kB 190.23 kB
react-native/implementations/ReactFabric-dev.fb.js +0.39% 890.46 kB 893.89 kB +0.61% 191.93 kB 193.11 kB
oss-stable-semver/react-art/umd/react-art.development.js +0.38% 908.82 kB 912.28 kB +0.59% 191.67 kB 192.80 kB
oss-stable/react-art/umd/react-art.development.js +0.38% 908.85 kB 912.31 kB +0.59% 191.69 kB 192.82 kB
react-native/implementations/ReactNativeRenderer-dev.fb.js +0.37% 906.15 kB 909.51 kB +0.57% 196.39 kB 197.52 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler.development.js +0.37% 893.99 kB 897.26 kB +0.57% 190.79 kB 191.89 kB
oss-stable/react-reconciler/cjs/react-reconciler.development.js +0.37% 894.02 kB 897.29 kB +0.57% 190.82 kB 191.91 kB
oss-experimental/react-dom/umd/react-dom.development.js +0.30% 1,327.96 kB 1,331.89 kB +0.47% 281.16 kB 282.48 kB
facebook-www/ReactDOM-dev.modern.js +0.29% 1,393.61 kB 1,397.73 kB +0.48% 300.29 kB 301.74 kB
oss-experimental/react-dom/cjs/react-dom.development.js +0.29% 1,266.48 kB 1,270.21 kB +0.46% 278.51 kB 279.80 kB
facebook-www/ReactDOMTesting-dev.modern.js +0.29% 1,412.02 kB 1,416.13 kB +0.46% 304.75 kB 306.16 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.development.js +0.29% 1,284.60 kB 1,288.32 kB +0.44% 282.88 kB 284.13 kB
facebook-www/ReactDOM-dev.classic.js +0.29% 1,421.51 kB 1,425.62 kB +0.48% 305.86 kB 307.32 kB
facebook-www/ReactDOMTesting-dev.classic.js +0.29% 1,439.91 kB 1,444.02 kB +0.44% 310.10 kB 311.46 kB
oss-stable-semver/react-dom/umd/react-dom.development.js +0.26% 1,313.56 kB 1,317.02 kB +0.42% 279.20 kB 280.37 kB
oss-stable/react-dom/umd/react-dom.development.js +0.26% 1,313.59 kB 1,317.04 kB +0.42% 279.23 kB 280.40 kB
oss-stable-semver/react-dom/cjs/react-dom.development.js +0.26% 1,252.79 kB 1,256.06 kB +0.41% 276.37 kB 277.50 kB
oss-stable/react-dom/cjs/react-dom.development.js +0.26% 1,252.82 kB 1,256.09 kB +0.41% 276.40 kB 277.53 kB

Generated by 🚫 dangerJS against 41dfb55

@acdlite acdlite force-pushed the initial-async-actions branch 2 times, most recently from a4b5955 to eab4260 Compare April 13, 2023 23:26
@acdlite acdlite requested a review from sebmarkbage April 13, 2023 23:26
@acdlite acdlite marked this pull request as ready for review April 13, 2023 23:26
Implements initial (client-only) support for async actions behind a
flag. This is an experimental feature and the design isn't completely
finalized but we're getting closer. It will be layered alongside other
features we're working on, so it may not feel complete when considered
in isolation.

The basic description is you can pass an async function to
`startTransition` and all the transition updates that are scheduled
inside that async function will be grouped together. The `isPending`
flag will be set to true immediately, and only set back to false once
the async action has completed (as well as all the updates that
it triggers).

The ideal behavior would be that all updates spawned by the async
action are automatically inferred and grouped together; however, doing
this properly requires the upcoming (stage 2) Async Context API, which
is not yet implemented by browsers. In the meantime, we will fake this
by grouping together all transition updates that occur until the async
function has terminated. This can lead to overgrouping between unrelated
actions, which is not wrong per se, just not ideal.

If the `useTransition` hook is removed from the UI before an async
action has completed — for example, if the user navigates to a new page
— subsequent transitions will no longer be grouped with together with
that action.

Another consequence of the lack of Async Context is that if you call
`setState` inside an action but after an `await`, it must be wrapped in
`startTransition` in order to be grouped properly. If we didn't require
this, then there would be no way to distinguish action updates from
urgent updates caused by user input, too. This is an unfortunate footgun
but we can likely detect the most common mistakes using a lint rule.

Once Async Context lands in browsers, we can start warning in dev if we
detect an update that hasn't been wrapped in `startTransition`. Then,
longer term, once the feature is ubiquitous, we can rely on it for real
and allow you to call `setState` without the additional wrapper.

Things that are _not_ yet implemented in this commit, but will be added
as follow ups:

- Support for non-hook from of `startTransition`
- Canceling the async action scope if the `useTransition` hook
  is deleted from the UI
- Anything related to server actions
@acdlite acdlite force-pushed the initial-async-actions branch from eab4260 to 41dfb55 Compare April 14, 2023 16:49
@acdlite acdlite merged commit cd2b79d into facebook:main Apr 19, 2023
kassens pushed a commit that referenced this pull request Apr 21, 2023
Implements initial (client-only) support for async actions behind a
flag. This is an experimental feature and the design isn't completely
finalized but we're getting closer. It will be layered alongside other
features we're working on, so it may not feel complete when considered
in isolation.

The basic description is you can pass an async function to
`startTransition` and all the transition updates that are scheduled
inside that async function will be grouped together. The `isPending`
flag will be set to true immediately, and only set back to false once
the async action has completed (as well as all the updates that it
triggers).

The ideal behavior would be that all updates spawned by the async action
are automatically inferred and grouped together; however, doing this
properly requires the upcoming (stage 2) Async Context API, which is not
yet implemented by browsers. In the meantime, we will fake this by
grouping together all transition updates that occur until the async
function has terminated. This can lead to overgrouping between unrelated
actions, which is not wrong per se, just not ideal.

If the `useTransition` hook is removed from the UI before an async
action has completed — for example, if the user navigates to a new page
— subsequent transitions will no longer be grouped with together with
that action.

Another consequence of the lack of Async Context is that if you call
`setState` inside an action but after an `await`, it must be wrapped in
`startTransition` in order to be grouped properly. If we didn't require
this, then there would be no way to distinguish action updates from
urgent updates caused by user input, too. This is an unfortunate footgun
but we can likely detect the most common mistakes using a lint rule.

Once Async Context lands in browsers, we can start warning in dev if we
detect an update that hasn't been wrapped in `startTransition`. Then,
longer term, once the feature is ubiquitous, we can rely on it for real
and allow you to call `setState` without the additional wrapper.

Things that are _not_ yet implemented in this PR, but will be added as
follow ups:

- Support for non-hook form of `startTransition`
- Canceling the async action scope if the `useTransition` hook is
deleted from the UI
- Anything related to server actions
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
Implements initial (client-only) support for async actions behind a
flag. This is an experimental feature and the design isn't completely
finalized but we're getting closer. It will be layered alongside other
features we're working on, so it may not feel complete when considered
in isolation.

The basic description is you can pass an async function to
`startTransition` and all the transition updates that are scheduled
inside that async function will be grouped together. The `isPending`
flag will be set to true immediately, and only set back to false once
the async action has completed (as well as all the updates that it
triggers).

The ideal behavior would be that all updates spawned by the async action
are automatically inferred and grouped together; however, doing this
properly requires the upcoming (stage 2) Async Context API, which is not
yet implemented by browsers. In the meantime, we will fake this by
grouping together all transition updates that occur until the async
function has terminated. This can lead to overgrouping between unrelated
actions, which is not wrong per se, just not ideal.

If the `useTransition` hook is removed from the UI before an async
action has completed — for example, if the user navigates to a new page
— subsequent transitions will no longer be grouped with together with
that action.

Another consequence of the lack of Async Context is that if you call
`setState` inside an action but after an `await`, it must be wrapped in
`startTransition` in order to be grouped properly. If we didn't require
this, then there would be no way to distinguish action updates from
urgent updates caused by user input, too. This is an unfortunate footgun
but we can likely detect the most common mistakes using a lint rule.

Once Async Context lands in browsers, we can start warning in dev if we
detect an update that hasn't been wrapped in `startTransition`. Then,
longer term, once the feature is ubiquitous, we can rely on it for real
and allow you to call `setState` without the additional wrapper.

Things that are _not_ yet implemented in this PR, but will be added as
follow ups:

- Support for non-hook form of `startTransition`
- Canceling the async action scope if the `useTransition` hook is
deleted from the UI
- Anything related to server actions
bigfootjon pushed a commit that referenced this pull request Apr 18, 2024
Implements initial (client-only) support for async actions behind a
flag. This is an experimental feature and the design isn't completely
finalized but we're getting closer. It will be layered alongside other
features we're working on, so it may not feel complete when considered
in isolation.

The basic description is you can pass an async function to
`startTransition` and all the transition updates that are scheduled
inside that async function will be grouped together. The `isPending`
flag will be set to true immediately, and only set back to false once
the async action has completed (as well as all the updates that it
triggers).

The ideal behavior would be that all updates spawned by the async action
are automatically inferred and grouped together; however, doing this
properly requires the upcoming (stage 2) Async Context API, which is not
yet implemented by browsers. In the meantime, we will fake this by
grouping together all transition updates that occur until the async
function has terminated. This can lead to overgrouping between unrelated
actions, which is not wrong per se, just not ideal.

If the `useTransition` hook is removed from the UI before an async
action has completed — for example, if the user navigates to a new page
— subsequent transitions will no longer be grouped with together with
that action.

Another consequence of the lack of Async Context is that if you call
`setState` inside an action but after an `await`, it must be wrapped in
`startTransition` in order to be grouped properly. If we didn't require
this, then there would be no way to distinguish action updates from
urgent updates caused by user input, too. This is an unfortunate footgun
but we can likely detect the most common mistakes using a lint rule.

Once Async Context lands in browsers, we can start warning in dev if we
detect an update that hasn't been wrapped in `startTransition`. Then,
longer term, once the feature is ubiquitous, we can rely on it for real
and allow you to call `setState` without the additional wrapper.

Things that are _not_ yet implemented in this PR, but will be added as
follow ups:

- Support for non-hook form of `startTransition`
- Canceling the async action scope if the `useTransition` hook is
deleted from the UI
- Anything related to server actions

DiffTrain build for commit cd2b79d.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants