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

Refactor Partial Hydration #16346

Merged
merged 13 commits into from
Aug 12, 2019
Merged

Conversation

sebmarkbage
Copy link
Collaborator

This is a pretty invasive refactor of the SuspenseComponent and DehydratedSuspenseComponent.

The primary purpose of this refactor is to avoid the hacky "upgrade" and "downgrade" by mutating the tag.

In the new model, a boundary is always represented by the same SuspenseComponent fiber. Inside it I store a "DehydratedFragment". The inner fiber represents the dehydrated nodes in the tree. This can be used to delete the whole thing or insert before it.

I also switched legacy mode to always client-render the boundary content since we can't hydrate partially in legacy mode. It also warns.

Otherwise, this (hopefully) shouldn't have an semantic differences.

@sizebot
Copy link

sizebot commented Aug 10, 2019

ReactDOM: size: 0.0%, gzip: 0.0%

Details of bundled changes.

Comparing: c203471...3243916

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom.profiling.min.js +0.3% +0.2% 114.66 KB 115.04 KB 36.2 KB 36.27 KB NODE_PROFILING
react-dom-server.browser.development.js 0.0% 0.0% 137.73 KB 137.73 KB 36.29 KB 36.29 KB UMD_DEV
ReactDOM-dev.js +0.3% +0.3% 931.18 KB 933.84 KB 205.94 KB 206.47 KB FB_WWW_DEV
react-dom-server.browser.production.min.js 0.0% 0.0% 19.26 KB 19.26 KB 7.22 KB 7.22 KB UMD_PROD
react-dom-test-utils.development.js 0.0% 0.0% 57.09 KB 57.09 KB 15.69 KB 15.69 KB UMD_DEV
react-dom-unstable-fizz.browser.development.js 0.0% +0.1% 3.78 KB 3.78 KB 1.53 KB 1.53 KB UMD_DEV
react-dom-test-utils.production.min.js 0.0% 🔺+0.1% 11.2 KB 11.2 KB 4.15 KB 4.15 KB UMD_PROD
react-dom-unstable-fizz.browser.production.min.js 0.0% 🔺+0.1% 1.2 KB 1.2 KB 702 B 703 B UMD_PROD
react-dom-test-utils.development.js 0.0% 0.0% 55.36 KB 55.36 KB 15.36 KB 15.36 KB NODE_DEV
react-dom-unstable-fizz.browser.development.js 0.0% +0.1% 3.61 KB 3.61 KB 1.48 KB 1.48 KB NODE_DEV
react-dom-test-utils.production.min.js 0.0% 0.0% 10.98 KB 10.98 KB 4.08 KB 4.08 KB NODE_PROD
react-dom-unstable-fizz.browser.production.min.js 0.0% 🔺+0.2% 1.04 KB 1.04 KB 633 B 634 B NODE_PROD
react-dom.development.js +0.3% +0.2% 907.07 KB 909.35 KB 205.95 KB 206.35 KB UMD_DEV
react-dom.production.min.js 🔺+0.3% 🔺+0.3% 111.06 KB 111.44 KB 35.79 KB 35.9 KB UMD_PROD
react-dom.profiling.min.js +0.3% +0.2% 114.46 KB 114.84 KB 36.81 KB 36.88 KB UMD_PROFILING
react-dom.development.js +0.3% +0.2% 901.37 KB 903.64 KB 204.36 KB 204.75 KB NODE_DEV
react-dom-server.node.development.js 0.0% 0.0% 134.88 KB 134.88 KB 35.58 KB 35.58 KB NODE_DEV
react-dom.production.min.js 🔺+0.3% 🔺+0.2% 111.02 KB 111.41 KB 35.24 KB 35.31 KB NODE_PROD
react-dom-server.node.production.min.js 0.0% 0.0% 19.6 KB 19.6 KB 7.37 KB 7.37 KB NODE_PROD
ReactDOM-prod.js -0.1% -0.1% 371.3 KB 370.95 KB 68.07 KB 68 KB FB_WWW_PROD
ReactDOM-profiling.js -0.0% -0.1% 375.98 KB 375.96 KB 69.06 KB 68.99 KB FB_WWW_PROFILING
react-dom-server.browser.production.min.js 0.0% 0.0% 19.19 KB 19.19 KB 7.21 KB 7.21 KB NODE_PROD
react-dom-unstable-native-dependencies.development.js 0.0% 0.0% 60.71 KB 60.71 KB 15.84 KB 15.85 KB UMD_DEV
react-dom-unstable-native-dependencies.production.min.js 0.0% 0.0% 10.75 KB 10.75 KB 3.68 KB 3.68 KB UMD_PROD
ReactDOMServer-dev.js 0.0% 0.0% 137.86 KB 137.86 KB 34.89 KB 34.9 KB FB_WWW_DEV
react-dom-unstable-native-dependencies.development.js 0.0% 0.0% 60.39 KB 60.39 KB 15.72 KB 15.72 KB NODE_DEV
react-dom-unstable-native-dependencies.production.min.js 0.0% 0.0% 10.49 KB 10.49 KB 3.58 KB 3.58 KB NODE_PROD
react-dom-unstable-fizz.node.production.min.js 0.0% 🔺+0.1% 1.1 KB 1.1 KB 668 B 669 B NODE_PROD

react-art

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-art.development.js +0.4% +0.3% 645.08 KB 647.38 KB 140.94 KB 141.31 KB UMD_DEV
react-art.production.min.js 🔺+0.1% -0.1% 101.67 KB 101.78 KB 31.06 KB 31.03 KB UMD_PROD
react-art.development.js +0.4% +0.3% 575.96 KB 578.24 KB 123.54 KB 123.9 KB NODE_DEV
react-art.production.min.js 🔺+0.2% -0.1% 66.68 KB 66.79 KB 20.31 KB 20.3 KB NODE_PROD
ReactART-dev.js +0.5% +0.4% 590.97 KB 593.63 KB 123.17 KB 123.7 KB FB_WWW_DEV
ReactART-prod.js 🔺+0.1% 🔺+0.1% 218.98 KB 219.17 KB 37.26 KB 37.29 KB FB_WWW_PROD

react-test-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
ReactTestRenderer-dev.js +0.4% +0.4% 602 KB 604.66 KB 125.61 KB 126.12 KB FB_WWW_DEV
react-test-renderer-shallow.development.js 0.0% 0.0% 39.01 KB 39.01 KB 9.91 KB 9.91 KB UMD_DEV
react-test-renderer-shallow.production.min.js 0.0% 0.0% 11.41 KB 11.41 KB 3.53 KB 3.53 KB UMD_PROD
react-test-renderer-shallow.development.js 0.0% 0.0% 33.18 KB 33.18 KB 8.5 KB 8.5 KB NODE_DEV
react-test-renderer.development.js +0.4% +0.3% 589.17 KB 591.45 KB 126.27 KB 126.66 KB UMD_DEV
react-test-renderer.production.min.js 🔺+0.2% -0.1% 68.6 KB 68.71 KB 21.1 KB 21.08 KB UMD_PROD
react-test-renderer.development.js +0.4% +0.3% 584.71 KB 586.98 KB 125.17 KB 125.55 KB NODE_DEV
react-test-renderer.production.min.js 🔺+0.2% -0.1% 68.31 KB 68.42 KB 20.82 KB 20.8 KB NODE_PROD

react-reconciler

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-reconciler.development.js +0.4% +0.3% 574.61 KB 576.88 KB 122.27 KB 122.64 KB NODE_DEV
react-reconciler.production.min.js 🔺+0.4% 🔺+0.1% 68.37 KB 68.67 KB 20.3 KB 20.31 KB NODE_PROD
react-reconciler-persistent.development.js +0.4% +0.3% 571.75 KB 574.02 KB 121.04 KB 121.41 KB NODE_DEV
react-reconciler-persistent.production.min.js 🔺+0.4% 🔺+0.1% 68.38 KB 68.68 KB 20.3 KB 20.32 KB NODE_PROD

react-native-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
ReactNativeRenderer-prod.js -0.0% -0.0% 269.32 KB 269.26 KB 46.13 KB 46.12 KB RN_OSS_PROD
ReactNativeRenderer-profiling.js -0.1% -0.1% 277.86 KB 277.71 KB 47.66 KB 47.63 KB RN_OSS_PROFILING
ReactFabric-prod.js -0.0% -0.0% 261.53 KB 261.47 KB 44.87 KB 44.85 KB RN_OSS_PROD
ReactFabric-profiling.js -0.1% 0.0% 270.29 KB 270.14 KB 46.35 KB 46.36 KB RN_OSS_PROFILING
ReactFabric-dev.js +0.4% +0.3% 733.33 KB 735.98 KB 155.06 KB 155.57 KB RN_FB_DEV
ReactFabric-prod.js -0.0% -0.0% 261.54 KB 261.48 KB 44.88 KB 44.86 KB RN_FB_PROD
ReactNativeRenderer-dev.js +0.4% +0.3% 723.7 KB 726.34 KB 153.34 KB 153.85 KB RN_OSS_DEV
ReactFabric-profiling.js -0.1% 0.0% 270.29 KB 270.14 KB 46.37 KB 46.37 KB RN_FB_PROFILING
ReactNativeRenderer-dev.js +0.4% +0.3% 723.86 KB 726.5 KB 153.42 KB 153.94 KB RN_FB_DEV
ReactNativeRenderer-prod.js -0.0% -0.0% 269.31 KB 269.25 KB 46.14 KB 46.13 KB RN_FB_PROD
ReactNativeRenderer-profiling.js -0.1% -0.1% 277.85 KB 277.7 KB 47.67 KB 47.64 KB RN_FB_PROFILING
ReactFabric-dev.js +0.4% +0.3% 733.16 KB 735.81 KB 154.98 KB 155.49 KB RN_OSS_DEV

Generated by 🚫 dangerJS

@@ -338,5 +338,7 @@
"337": "An invalid event responder was provided to host component",
"338": "ReactDOMServer does not yet support the fundamental API.",
"339": "An invalid value was used as an event responder. Expect one or many event responders created via React.unstable_createResponer().",
"340": "An invalid value was used as an event listener. Expect one or many event listeners created via React.unstable_useResponer()."
"340": "An invalid value was used as an event listener. Expect one or many event listeners created via React.unstable_useResponer().",
Copy link
Collaborator

Choose a reason for hiding this comment

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

@trueadm Noticed a typo here. Should be unstable_useResponder()

Copy link
Contributor

Choose a reason for hiding this comment

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

I’m on PTO. cc @necolas

Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh yeah, sorry. I mean I can do it too :D

Copy link
Collaborator

Choose a reason for hiding this comment

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

if (current === null || current.memoizedState !== null) {
if (
current === null ||
(current.memoizedState: null | SuspenseState) !== null
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this necessary? ^

Copy link
Collaborator Author

@sebmarkbage sebmarkbage Aug 12, 2019

Choose a reason for hiding this comment

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

It's so that it becomes greppable.

Once we add types for fields like memoizedState it will also be used to ensure that those types are correct, as we make that change.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The type SuspenseState should probably be nullable by itself since it has meaning and would be a variant if we had those.

We now store the comment node on SuspenseState instead and that indicates
that this SuspenseComponent is still dehydrated.

We also store a child but that is only used to represent the DOM node for
deletions and getNextHostSibling.
Forked based on SuspenseState.dehydrated instead.
We can now simplify the logic for retrying dehydrated boundaries without
hydrating. This is becomes simply a reconciliation against the dehydrated
fragment which gets deleted, and the new children gets inserted.
Instead we use the regular Suspense path. To save code, we attach retry
listeners in the commit phase even though technically we don't have to.
I think this is right...?
The DidCapture flag isn't used consistently in the same way. We need
further refactor for this.
If we remove the dehydration status in the first pass and then do a second
pass because we suspended, then we need to continue as if it didn't
previously suspend. Since there is no fragment child etc.

However, we must readd the deletion.
This now doesn't represent a suspense boundary itself. Its parent does.

This Fiber represents the fragment around the dehydrated content.
It does a two pass render that client renders the content.
Avoids the temporary mutable variables. I kept losing track of them.
Placing it in the type since that's the central point as opposed to spread
out.
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.

6 participants