-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Experimental support for resuming suspended hydration #2214
base: main
Are you sure you want to change the base?
Conversation
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 really good to me
@@ -215,6 +225,11 @@ export function diff( | |||
|
|||
if ((tmp = options.diffed)) tmp(newVNode); | |||
} catch (e) { | |||
// Before bailing out, mark the current VNode with the DOM element and hydration state. | |||
// We can use this information if we return here to render later on. | |||
newVNode._dom = oldDom; |
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.
Can be reduced to:
newVNode._dom = oldDom; | |
oldVNode._hydrateDom = newVNode._dom = oldDom; |
This removes the need for the next line (multi-line suggestions aren't available)
// If this was the innermost VNode at a point where the tree suspended, | ||
// pick up diffing where we left off using the saved DOM element and hydration state. | ||
if (oldVNode._hydrateDom && excessDomChildren == null) { | ||
oldDom = oldVNode._hydrateDom; |
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.
oldDom = oldVNode._hydrateDom; | |
newVNode._dom = oldDom = oldVNode._hydrateDom; |
I tried reproducing the broken test in a real world app but couldn't everything worked fine there |
@developit am i supposed to see this with your given instructions? |
Size Change: +188 B (0%) Total Size: 38.3 kB
ℹ️ View Unchanged
|
This PR adds two metadata properties to VNodes which are used to store the
isHydrating
flag and current DOM element (oldDom
) when bailing out of diffing as a result of a suspension (or error).This is particularly useful during hydration, as it means we can flip back into hydration mode and pick up the previously-attempted DOM element when we attempt to render the previously-suspended tree's root VNode.
The implementation here is not great in terms of filesize, and I haven't added dedicated tests for it yet. I have verified that this works with a prototype Suspense-based
AsyncComponent
implementation for Preact CLI (included below) that can be tested vianpm link
here.experimental Suspense-based
async-component.js
for preact cliThe main thing worth noting here is that there is no use of internals.
How it works: the generated wrapper component (
AsyncComponent
) renders a child component that it knows will throw a given value (an object for simple referential equality). It then checks for that value having been thrown incomponentDidCatch
and absorbs the error, which has the effect of dropping its subtree's rendering on the floor. This preserves the DOM tree in-tact during the period where the lazy-loaded component is not yet available.It perfom this throw routing for all renders up until its payload (whatever is passed to the callback given to
load
) becomes available. Once available, it "unsuspends" by triggering a re-render usingsetState({})
. This is where the magic happens:Since Preact knows the original "owner" VNode behind this component was suspended (in hydrate mode!) and has maintained a reference to its associated DOM element at time of suspension, it will immediately jump back into hydrate mode in the
diff()
call originating from setState and re-use that stored DOM element.