diff --git a/compat/test/browser/suspense-hydration.test.js b/compat/test/browser/suspense-hydration.test.js index 9143903238..43b43b552c 100644 --- a/compat/test/browser/suspense-hydration.test.js +++ b/compat/test/browser/suspense-hydration.test.js @@ -139,6 +139,75 @@ describe('suspense hydration', () => { }); }); + it('Should hydrate a fragment with multiple children correctly', () => { + scratch.innerHTML = '
Hello
World!
'; + clearLog(); + + const [Lazy, resolve] = createLazy(); + hydrate( + + + , + scratch + ); + rerender(); // Flush rerender queue to mimic what preact will really do + expect(scratch.innerHTML).to.equal('
Hello
World!
'); + expect(getLog()).to.deep.equal([]); + clearLog(); + + return resolve(() => ( + <> +
Hello
+
World!
+ + )).then(() => { + rerender(); + expect(scratch.innerHTML).to.equal('
Hello
World!
'); + expect(getLog()).to.deep.equal([]); + + clearLog(); + }); + }); + + // This is an expected fail case, we should encourage folks to not have adjacent + // nodes of the same type, or return a single wrapping dom node from a lazy boundary. + it.skip('Should hydrate a fragment with multiple children and an adjacent node correctly', () => { + scratch.innerHTML = '
Hello
World!
Baz
'; + clearLog(); + + const [Lazy, resolve] = createLazy(); + hydrate( + <> + + + +
Baz
+ , + scratch + ); + rerender(); // Flush rerender queue to mimic what preact will really do + expect(scratch.innerHTML).to.equal( + '
Hello
World!
Baz
' + ); + expect(getLog()).to.deep.equal([]); + clearLog(); + + return resolve(() => ( + <> +
Hello
+
World!
+ + )).then(() => { + rerender(); + expect(scratch.innerHTML).to.equal( + '
Hello
World!
Baz
' + ); + expect(getLog()).to.deep.equal([]); + + clearLog(); + }); + }); + it('should allow siblings to update around suspense boundary', () => { scratch.innerHTML = '
Count: 0
Hello
'; clearLog(); diff --git a/jsx-runtime/src/index.js b/jsx-runtime/src/index.js index 454df6308a..98551fc28a 100644 --- a/jsx-runtime/src/index.js +++ b/jsx-runtime/src/index.js @@ -59,6 +59,7 @@ function createVNode(type, props, key, isStaticChildren, __source, __self) { _nextDom: undefined, _component: null, constructor: undefined, + _excess: null, _original: --vnodeId, _index: -1, _flags: 0, diff --git a/mangle.json b/mangle.json index fcd2dce74f..4bb11b1fae 100644 --- a/mangle.json +++ b/mangle.json @@ -27,6 +27,7 @@ "props": { "$_listeners": "l", "$_cleanup": "__c", + "$_excess": "__x", "$__hooks": "__H", "$_list": "__", "$_pendingEffects": "__h", diff --git a/src/create-element.js b/src/create-element.js index 66898b2224..6308287d7a 100644 --- a/src/create-element.js +++ b/src/create-element.js @@ -62,6 +62,7 @@ export function createVNode(type, props, key, ref, original) { props, key, ref, + _excess: null, _children: null, _parent: null, _depth: 0, diff --git a/src/diff/index.js b/src/diff/index.js index 3e4b17bd62..35a9b7c6d2 100644 --- a/src/diff/index.js +++ b/src/diff/index.js @@ -53,7 +53,7 @@ export function diff( if (oldVNode._flags & MODE_SUSPENDED) { isHydrating = !!(oldVNode._flags & MODE_HYDRATE); oldDom = newVNode._dom = oldVNode._dom; - excessDomChildren = [oldDom]; + excessDomChildren = oldVNode._excess; } if ((tmp = options._diff)) tmp(newVNode); @@ -277,6 +277,7 @@ export function diff( newVNode._flags |= isHydrating ? MODE_HYDRATE | MODE_SUSPENDED : MODE_HYDRATE; + newVNode._excess = [...excessDomChildren]; excessDomChildren[excessDomChildren.indexOf(oldDom)] = null; // ^ could possibly be simplified to: // excessDomChildren.length = 0; diff --git a/src/internal.d.ts b/src/internal.d.ts index cbf23b3888..e771e8e679 100644 --- a/src/internal.d.ts +++ b/src/internal.d.ts @@ -141,6 +141,7 @@ declare global { _children: Array> | null; _parent: VNode | null; _depth: number | null; + _excess: Array | null; /** * The [first (for Fragments)] DOM child of a VNode */