-
Notifications
You must be signed in to change notification settings - Fork 47k
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
Sync hydrate discrete events in capture phase and dont replay discrete events #22448
Sync hydrate discrete events in capture phase and dont replay discrete events #22448
Conversation
Comparing: a4bc8ae...c10215e Critical size changesIncludes critical production bundles, as well as any change greater than 2%:
Significant size changesIncludes any change greater than 0.2%: Expand to show
|
Ping @gaearon @sebastianopeluso . Basically just deleted the discrete event replaying and moved the synchronous hydration block from |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great! Left some comments, and also think we should consider running this change in an experiment. Will followup offline 👍
packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js
Outdated
Show resolved
Hide resolved
packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js
Outdated
Show resolved
Hide resolved
packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js
Outdated
Show resolved
Hide resolved
packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js
Outdated
Show resolved
Hide resolved
Gated the changes so we can test it. Gated some of the tests too but for some I checked the flag within the test since I didn't want to invalidate the whole test when the flag was on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's try it, but please fix up reduced coverage and skip unrelated changes before merging
packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js
Outdated
Show resolved
Hide resolved
packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js
Outdated
Show resolved
Hide resolved
packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js
Outdated
Show resolved
Hide resolved
packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js
Outdated
Show resolved
Hide resolved
packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js
Outdated
Show resolved
Hide resolved
packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js
Outdated
Show resolved
Hide resolved
packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js
Outdated
Show resolved
Hide resolved
packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js
Outdated
Show resolved
Hide resolved
What happens if we're not ready to hydrate? Do we prevent the event propagation in the capture phase? I think we planned to do this but I'm not sure if that's still the plan, and whether this is how it works in this PR. |
Yeah thats a mistake on my part I should have added that in the PR too but it felt like we were less confident about that part of the change. Here are the notes from when we spoke about it:
So It sounds like we wanted to test:
This current PR does (2) but I can put up a follow up PR to test (1) first since thats what we agreed to |
I'm a bit confused by what you mean by "to test". I'm speaking more about the behavior itself, not only the tests. What is the current behavior, and what is the behavior that was agreed to? I wasn't actively tracking the discussion so maybe it would be good if you could confirm with Seb what the latest decision was. Either it should stop propagation during unhydrated capture, or it should not. |
I think he meant "to test" as in "to try". I think that's the decision. To see what happens in practice. I think we'll want to try (1) instead though. Allowing it to continue means that it doesn't just bubble up the parent but also down the children. I think we'll want to add a test that the parent listener doesn't ever fire in this case: let triggeredParent = false;
...
<div onClick={() => triggeredParent = true}>
<Suspense fallback={null}>
<div onClick={e => e.stopPropagation()} ref={ref} />
</Suspense>
</div>
...
ref.current.dispatchEvent(...);
...
expect(triggeredParent).toBe(false); These days, so many apps use React all the way to the |
Ahh ok I get it now. 😛 So the conclusion is let's do a follow-up for (1) with the test you described before enabling the flag. |
Interestingly I wrote the test but it already passes without any other changes🤔 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's try rolling this out quickly and then move on to cleaning up the code. Because the code structure now is way over-engineered for what it's actually doing and keeping this structure will just lead to confusion and dogma about keeping the structure.
I'd like to see what this looks like with all the unnecessary code removed and the attempt refactored.
Also, the goal is to eventually get rid of the synthetic event system. This dispatch function only exists for that purposes.
To explain this layering it would be good if that all the sync hydration stuff was the first thing that happened in the listener without early bailout - or even it's own listener. That way we know that the system behaves the same with React events as with other event listeners.
For example the whole attemptToDispatchEvent and blockedOn structure probably doesn't make sense anymore.
Summary: This sync includes the following changes: - **[579c008a7](facebook/react@579c008a7 )**: [Fizz/Flight] pipeToNodeWritable(..., writable).startWriting() -> renderToPipeableStream(...).pipe(writable) ([#22450](facebook/react#22450)) //<Sebastian Markbåge>// - **[f2c381131](facebook/react@f2c381131 )**: fix: useSyncExternalStoreExtra ([#22500](facebook/react#22500)) //<Daishi Kato>// - **[0ecbbe142](facebook/react@0ecbbe142 )**: Sync hydrate discrete events in capture phase and dont replay discrete events ([#22448](facebook/react#22448)) //<salazarm>// - **[a724a3b57](facebook/react@a724a3b57 )**: [RFC] Codemod invariant -> throw new Error ([#22435](facebook/react#22435)) //<Andrew Clark>// - **[201af81b0](facebook/react@201af81b0 )**: Release pooled cache reference in complete/unwind ([#22464](facebook/react#22464)) //<Joseph Savona>// - **[033efe731](facebook/react@033efe731 )**: Call get snapshot in useSyncExternalStore server shim ([#22453](facebook/react#22453)) //<salazarm>// - **[7843b142a](facebook/react@7843b142a )**: [Fizz/Flight] Pass in Destination lazily to startFlowing instead of in createRequest ([#22449](facebook/react#22449)) //<Sebastian Markbåge>// - **[d9fb383d6](facebook/react@d9fb383d6 )**: Extract queueing logic into shared functions ([#22452](facebook/react#22452)) //<Andrew Clark>// - **[9175f4d15](facebook/react@9175f4d15 )**: Scheduling Profiler: Show Suspense resource .displayName ([#22451](facebook/react#22451)) //<Brian Vaughn>// - **[eba248c39](facebook/react@eba248c39 )**: [Fizz/Flight] Remove reentrancy hack ([#22446](facebook/react#22446)) //<Sebastian Markbåge>// - **[66388150e](facebook/react@66388150e )**: Remove usereducer eager bailout ([#22445](facebook/react#22445)) //<Joseph Savona>// - **[d3e086932](facebook/react@d3e086932 )**: Make root.unmount() synchronous ([#22444](facebook/react#22444)) //<Andrew Clark>// - **[2cc6d79c9](facebook/react@2cc6d79c9 )**: Rename onReadyToStream to onCompleteShell ([#22443](facebook/react#22443)) //<Sebastian Markbåge>// - **[c88fb49d3](facebook/react@c88fb49d3 )**: Improve DEV errors if string coercion throws (Temporal.*, Symbol, etc.) ([#22064](facebook/react#22064)) //<Justin Grant>// - **[05726d72c](facebook/react@05726d72c )**: [Fix] Errors should not "unsuspend" a transition ([#22423](facebook/react#22423)) //<Andrew Clark>// - **[3746eaf98](facebook/react@3746eaf98 )**: Packages/React/src/ReactLazy ---> changing -1 to unintialized ([#22421](facebook/react#22421)) //<BIKI DAS>// - **[04ccc01d9](facebook/react@04ccc01d9 )**: Hydration errors should force a client render ([#22416](facebook/react#22416)) //<Andrew Clark>// - **[029fdcebb](facebook/react@029fdcebb )**: root.hydrate -> root.isDehydrated ([#22420](facebook/react#22420)) //<Andrew Clark>// - **[af87f5a83](facebook/react@af87f5a83 )**: Scheduling Profiler marks should include thrown Errors ([#22417](facebook/react#22417)) //<Brian Vaughn>// - **[d47339ea3](facebook/react@d47339ea3 )**: [Fizz] Remove assignID mechanism ([#22410](facebook/react#22410)) //<Sebastian Markbåge>// - **[3a50d9557](facebook/react@3a50d9557 )**: Never attach ping listeners in legacy Suspense ([#22407](facebook/react#22407)) //<Andrew Clark>// - **[82c8fa90b](facebook/react@82c8fa90b )**: Add back useMutableSource temporarily ([#22396](facebook/react#22396)) //<Andrew Clark>// - **[5b57bc6e3](facebook/react@5b57bc6e3 )**: [Draft] don't patch console during first render ([#22308](facebook/react#22308)) //<Luna Ruan>// - **[cf07c3df1](facebook/react@cf07c3df1 )**: Delete all but one `build2` reference ([#22391](facebook/react#22391)) //<Andrew Clark>// - **[bb0d06935](facebook/react@bb0d06935 )**: [build2 -> build] Local scripts //<Andrew Clark>// - **[0c81d347b](facebook/react@0c81d347b )**: Write artifacts to `build` instead of `build2` //<Andrew Clark>// - **[4da03c9fb](facebook/react@4da03c9fb )**: useSyncExternalStore React Native version ([#22367](facebook/react#22367)) //<salazarm>// - **[48d475c9e](facebook/react@48d475c9e )**: correct typos ([#22294](facebook/react#22294)) //<Bowen>// - **[cb6c619c0](facebook/react@cb6c619c0 )**: Remove Fiber fields that were used for hydrating useMutableSource ([#22368](facebook/react#22368)) //<Sebastian Silbermann>// - **[64e70f82e](facebook/react@64e70f82e )**: [Fizz] add avoidThisFallback support ([#22318](facebook/react#22318)) //<salazarm>// - **[3ee7a004e](facebook/react@3ee7a004e )**: devtools: Display actual ReactDOM API name in root type ([#22363](facebook/react#22363)) //<Sebastian Silbermann>// - **[79b8fc667](facebook/react@79b8fc667 )**: Implement getServerSnapshot in userspace shim ([#22359](facebook/react#22359)) //<Andrew Clark>// - **[86b3e2461](facebook/react@86b3e2461 )**: Implement useSyncExternalStore on server ([#22347](facebook/react#22347)) //<Andrew Clark>// - **[8209de269](facebook/react@8209de269 )**: Delete useMutableSource implementation ([#22292](facebook/react#22292)) //<Andrew Clark>// Changelog: [General][Changed] - React Native sync for revisions e8feb11...afcb9cd jest_e2e[run_all_tests] Reviewed By: yungsters Differential Revision: D31541359 fbshipit-source-id: c35941bc303fdf55cb061e9996200dc868a6f2af
Summary
This PR makes it so that discrete events are not replayed because there is a lot of ambiguity regarding the priority of the events and trying to enforce the order of the discrete events leads to being suboptimal with CPU. Instead we attempt to synchronously hydrate discrete events in the capture phase of the event (on the React root) in time for their capture and bubble listeners to fire naturally.
Continuous events are unaffected and will continue being sync hydrated in bubble phase if possible and replayed.
How did you test this change?
Jest.
I left the code ungated to make it easier to review but after review I will fork the codepaths and tests and put them behind a flag so that we can test the changes.