diff --git a/compat/src/suspense.js b/compat/src/suspense.js index 9229774f91..48c8960996 100644 --- a/compat/src/suspense.js +++ b/compat/src/suspense.js @@ -1,5 +1,9 @@ import { Component, createElement, options, Fragment } from 'preact'; -import { MODE_HYDRATE, COMPONENT_FORCE } from '../../src/constants'; +import { + MODE_HYDRATE, + FORCE_PROPS_REVALIDATE, + COMPONENT_FORCE +} from '../../src/constants'; import { assign } from './util'; const oldCatchError = options._catchError; @@ -67,6 +71,10 @@ function detachedClone(vnode, detachedParent, parentDom) { function removeOriginal(vnode, detachedParent, originalParent) { if (vnode && originalParent) { + if (typeof vnode.type == 'string') { + vnode._flags |= FORCE_PROPS_REVALIDATE; + } + vnode._original = null; vnode._children = vnode._children && diff --git a/compat/test/browser/suspense-hydration.test.js b/compat/test/browser/suspense-hydration.test.js index 59d3117adf..6a02112bf0 100644 --- a/compat/test/browser/suspense-hydration.test.js +++ b/compat/test/browser/suspense-hydration.test.js @@ -656,6 +656,14 @@ describe('suspense hydration', () => { rerender(); expect(scratch.innerHTML, 'second suspend').to.equal(div('fallback')); + expect(getLog()).to.deep.equal([ + '
b1.remove()', + '
a.remove()', + '
c.remove()', + '
.appendChild(#text)', + '
.appendChild(
fallback)' + ]); + clearLog(); return resolve2(() =>
b2
); }) @@ -664,12 +672,20 @@ describe('suspense hydration', () => { expect(scratch.innerHTML, 'second suspend resumes').to.equal( [div('a'), div('b2'), div('c')].join('') ); - - scratch.lastChild.dispatchEvent(createEvent('click')); - expect(cOnClickSpy).toHaveBeenCalledTimes(2); + expect(getLog()).to.deep.equal([ + '
fallback.appendChild(
a)', + '
fallback.remove()', + '
a.appendChild(
a)', + '
.appendChild(#text)', + '
a.appendChild(
b2)', + '
ab2.appendChild(
c)' + ]); scratch.firstChild.nextSibling.dispatchEvent(createEvent('click')); expect(bOnClickSpy).toHaveBeenCalledTimes(2); + + scratch.lastChild.dispatchEvent(createEvent('click')); + expect(cOnClickSpy).toHaveBeenCalledTimes(2); }); }); diff --git a/src/constants.js b/src/constants.js index 61e6500693..7a823a3efd 100644 --- a/src/constants.js +++ b/src/constants.js @@ -7,6 +7,8 @@ export const MODE_SUSPENDED = 1 << 7; export const INSERT_VNODE = 1 << 2; /** Indicates a VNode has been matched with another VNode in the diff */ export const MATCHED = 1 << 1; +/** Indicates that this vnode has been unmounted before indicating the loss of event listeners */ +export const FORCE_PROPS_REVALIDATE = 1 << 0; // component._bits /** Component is processing an exception */ diff --git a/src/diff/index.js b/src/diff/index.js index f8d0e7d36a..7cffa6d7ea 100644 --- a/src/diff/index.js +++ b/src/diff/index.js @@ -4,6 +4,7 @@ import { COMPONENT_PENDING_ERROR, COMPONENT_PROCESSING_EXCEPTION, EMPTY_OBJ, + FORCE_PROPS_REVALIDATE, MATH_NAMESPACE, MODE_HYDRATE, MODE_SUSPENDED, @@ -388,7 +389,7 @@ export function diff( if ((tmp = options.diffed)) tmp(newVNode); - return newVNode._flags & MODE_SUSPENDED ? undefined : oldDom; + return newVNode._flags & MODE_SUSPENDED ? UNDEFINED : oldDom; } function markAsForce(vnode) { @@ -566,6 +567,7 @@ function diffElementNodes( // During hydration, props are not diffed at all (including dangerouslySetInnerHTML) // @TODO we should warn in debug mode when props don't match here. + const shouldRevalidateProps = oldVNode._flags & FORCE_PROPS_REVALIDATE; for (i in newProps) { value = newProps[i]; if (i == 'children') { @@ -578,7 +580,7 @@ function diffElementNodes( checked = value; } else if ( (!isHydrating || typeof value == 'function') && - oldProps[i] !== value + (oldProps[i] !== value || shouldRevalidateProps) ) { setProperty(dom, i, value, oldProps[i], namespace); } @@ -723,7 +725,11 @@ export function unmount(vnode, parentVNode, skipRemove) { removeNode(vnode._dom); } - vnode._component = vnode._parent = vnode._dom = UNDEFINED; + if (vnode._dom && vnode._dom._listeners) { + vnode._dom._listeners = NULL; + } + + vnode._dom = vnode._component = vnode._parent = NULL; } /** The `.render()` method for a PFC backing instance. */