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

Synchronously flush the transition lane scheduled in a popstate event #26025

Merged
merged 7 commits into from
Apr 13, 2023

Conversation

tyao1
Copy link
Contributor

@tyao1 tyao1 commented Jan 20, 2023

Summary

Browsers restore state like forms and scroll position right after the popstate event. To make sure the page work as expected on back or forward button, we need to flush transitions scheduled in a popstate synchronously, and only yields if it suspends.
This PR adds a new HostConfig method to check if window.event === 'popstate', and scheduleMicrotask if a transition is scheduled in a PopStateEvent.

How did you test this change?

yarn test

@tyao1 tyao1 requested review from sebmarkbage and acdlite January 20, 2023 03:28
@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Jan 20, 2023
@@ -259,6 +259,8 @@ type BaseFiberRootProperties = {
pooledCache: Cache | null,
pooledCacheLanes: Lanes,

forceSync: boolean,
Copy link
Collaborator

Choose a reason for hiding this comment

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

It's not really a "forced" sync because it just an best-effort attempt. This could easily be confused with the API called ReactDOM.flushSync(...) which is more like a force.

This would be more like attemptEagerTransition. I don't think you should necessarily need this flag though with the new refactoring because once we schedule an eager microtask all the time, you can just do the work right there if you check for it.

if (shouldAttemptEagerTransition()) performWorkOnRoot()

A lot of things will be a lot simpler with that model.

@@ -1469,7 +1478,7 @@ function performSyncWorkOnRoot(root: FiberRoot) {
flushPassiveEffects();

let lanes = getNextLanes(root, NoLanes);
if (!includesSyncLane(lanes)) {
if (!includesSyncLaneOrForceSync(lanes, root)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of using performSyncWorkOnRoot for this, I wonder if you can just use performConcurrentWorkOnRoot without yielding. That way you don't have to mess with the lanes and other special cases for sync.

We already have cases for performing concurrent work without yielding.

// We disable time-slicing in some cases: if the work has been CPU-bound
// for too long ("expired" work, to prevent starvation), or we're in
// sync-updates-by-default mode.
// TODO: We only check `didTimeout` defensively, to account for a Scheduler
// bug we're still investigating. Once the bug in Scheduler is fixed,
// we can remove this, since we track expiration ourselves.

@tyao1 tyao1 force-pushed the ty-popstate-transition branch from ffb7566 to 1bceba2 Compare April 4, 2023 02:20
@react-sizebot
Copy link

react-sizebot commented Apr 4, 2023

Comparing: 72c890e...b340309

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.04% 164.40 kB 164.47 kB +0.06% 51.65 kB 51.68 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js +0.04% 166.84 kB 166.91 kB +0.06% 52.30 kB 52.33 kB
facebook-www/ReactDOM-prod.classic.js +0.03% 565.32 kB 565.49 kB +0.05% 99.49 kB 99.55 kB
facebook-www/ReactDOM-prod.modern.js +0.03% 549.11 kB 549.28 kB +0.06% 96.79 kB 96.85 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-noop-renderer/cjs/react-noop-renderer.production.min.js +0.32% 15.57 kB 15.62 kB +0.37% 4.66 kB 4.67 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer.production.min.js +0.32% 15.57 kB 15.62 kB +0.37% 4.66 kB 4.67 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer.production.min.js +0.32% 15.57 kB 15.62 kB +0.37% 4.66 kB 4.67 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-persistent.production.min.js +0.32% 15.64 kB 15.69 kB +0.34% 4.67 kB 4.69 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-persistent.production.min.js +0.32% 15.64 kB 15.69 kB +0.34% 4.67 kB 4.69 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-persistent.production.min.js +0.32% 15.64 kB 15.69 kB +0.34% 4.67 kB 4.69 kB

Generated by 🚫 dangerJS against b340309

@tyao1 tyao1 force-pushed the ty-popstate-transition branch from 1bceba2 to 638cd73 Compare April 4, 2023 02:47
@tyao1 tyao1 force-pushed the ty-popstate-transition branch from cd4e4cc to f5622fa Compare April 5, 2023 23:10
@tyao1 tyao1 requested a review from acdlite April 10, 2023 16:41
Copy link
Collaborator

@sophiebits sophiebits left a comment

Choose a reason for hiding this comment

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

Worth adding a test for the behavior where the transition can’t finish synchronously without triggering a fallback?

@tyao1 tyao1 force-pushed the ty-popstate-transition branch from f5622fa to f642605 Compare April 12, 2023 19:38
@tyao1
Copy link
Contributor Author

tyao1 commented Apr 12, 2023

Worth adding a test for the behavior where the transition can’t finish synchronously without triggering a fallback?

I'm not sure what would the expected behavior be when that happens. Maybe we can address it in a follow up PR.

@sophiebits
Copy link
Collaborator

My understanding of the original goal was that if it can't finish the transition synchronously then the transition should suspend and not trigger any fallbacks. So it would fail to finish synchronously but the fallback behavior would be the same as if it didn't try for the eager transition. That's why it is called "should attempt eager transition" not "should force eager transition" or something. It's a nice to have for the common case so that browser scroll restoration, etc. works correctly but if for some reason the data isn't available then it would do the async transition. However my knowledge may be out of date so definitely defer to others. Also I don't understand how that intersects with async components since it seems like those could never finish synchronously? (I guess someone could cache their previous output somehow?)

Either way, it seems like an important aspect of the behavior here so I think it would be good to encode it in a test.

@acdlite
Copy link
Collaborator

acdlite commented Apr 13, 2023

My understanding of the original goal was that if it can't finish the transition synchronously then the transition should suspend and not trigger any fallbacks.

The thinking is that it's more important for it to be synchronous than to avoid a fallback in this case because 1) it needs to be sync in order for the browser's scroll restoration mechanism to work 2) during a back navigation it's likely the data is cached so it probably won't suspend the important stuff anyway.

However, if something suspends and there's no Suspense boundary above that can handle it, this trick isn't going to work regardless, so the thinking was we would fall back to the regular transition behavior (give up and change it back to async). This PR doesn't implement that but it's not as urgent as the main fix and we can add that later.

@acdlite
Copy link
Collaborator

acdlite commented Apr 13, 2023

I need this for another feature I'm working on so I'm going to go ahead and merge it, if that's alright

@acdlite acdlite merged commit d121c67 into main Apr 13, 2023
github-actions bot pushed a commit that referenced this pull request Apr 13, 2023
…#26025)

<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn debug-test --watch TestName`, open
`chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

Browsers restore state like forms and scroll position right after the
popstate event. To make sure the page work as expected on back or
forward button, we need to flush transitions scheduled in a popstate
synchronously, and only yields if it suspends.
This PR adds a new HostConfig method to check if `window.event ===
'popstate'`, and `scheduleMicrotask` if a transition is scheduled in a
`PopStateEvent`.

## How did you test this change?

yarn test

DiffTrain build for [d121c67](d121c67)
kassens added a commit to kassens/react that referenced this pull request Apr 17, 2023
kassens pushed a commit that referenced this pull request Apr 21, 2023
…#26025)

<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn debug-test --watch TestName`, open
`chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

Browsers restore state like forms and scroll position right after the
popstate event. To make sure the page work as expected on back or
forward button, we need to flush transitions scheduled in a popstate
synchronously, and only yields if it suspends.
This PR adds a new HostConfig method to check if `window.event ===
'popstate'`, and `scheduleMicrotask` if a transition is scheduled in a
`PopStateEvent`.

## How did you test this change?

yarn test
@tyao1 tyao1 deleted the ty-popstate-transition branch October 12, 2023 17:39
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
…facebook#26025)

<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn debug-test --watch TestName`, open
`chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

Browsers restore state like forms and scroll position right after the
popstate event. To make sure the page work as expected on back or
forward button, we need to flush transitions scheduled in a popstate
synchronously, and only yields if it suspends.
This PR adds a new HostConfig method to check if `window.event ===
'popstate'`, and `scheduleMicrotask` if a transition is scheduled in a
`PopStateEvent`.

## How did you test this change?

yarn test
bigfootjon pushed a commit that referenced this pull request Apr 18, 2024
…#26025)

<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn debug-test --watch TestName`, open
`chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

Browsers restore state like forms and scroll position right after the
popstate event. To make sure the page work as expected on back or
forward button, we need to flush transitions scheduled in a popstate
synchronously, and only yields if it suspends.
This PR adds a new HostConfig method to check if `window.event ===
'popstate'`, and `scheduleMicrotask` if a transition is scheduled in a
`PopStateEvent`.

## How did you test this change?

yarn test

DiffTrain build for commit d121c67.
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.

7 participants