From 60cf06769a4cbc3de8323117684db8701664f440 Mon Sep 17 00:00:00 2001 From: Rairn <958414905@qq.com> Date: Sun, 9 Oct 2022 12:05:14 +0800 Subject: [PATCH 1/4] fix(Transition): should not be updated again after unmounted (#6835) --- packages/runtime-core/src/components/BaseTransition.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/src/components/BaseTransition.ts b/packages/runtime-core/src/components/BaseTransition.ts index 5e5e216d34a..4b654812ead 100644 --- a/packages/runtime-core/src/components/BaseTransition.ts +++ b/packages/runtime-core/src/components/BaseTransition.ts @@ -238,7 +238,8 @@ const BaseTransitionImpl: ComponentOptions = { // return placeholder node and queue update when leave finishes leavingHooks.afterLeave = () => { state.isLeaving = false - instance.update() + // #6835 + instance.update.active !== false && instance.update() } return emptyPlaceholder(child) } else if (mode === 'in-out' && innerChild.type !== Comment) { From 66ac9d02741f620de749f2c915beef82b4d93e62 Mon Sep 17 00:00:00 2001 From: Rairn <958414905@qq.com> Date: Sun, 9 Oct 2022 20:10:02 +0800 Subject: [PATCH 2/4] test(Transition): mode: out-in toggle again after unmounted --- .../components/BaseTransition.spec.ts | 122 +++++++++++++++--- 1 file changed, 106 insertions(+), 16 deletions(-) diff --git a/packages/runtime-core/__tests__/components/BaseTransition.spec.ts b/packages/runtime-core/__tests__/components/BaseTransition.spec.ts index ade8fffa7d7..7b2fb389540 100644 --- a/packages/runtime-core/__tests__/components/BaseTransition.spec.ts +++ b/packages/runtime-core/__tests__/components/BaseTransition.spec.ts @@ -19,13 +19,20 @@ function mount( withKeepAlive = false ) { const root = nodeOps.createElement('div') - render( - h(BaseTransition, props, () => { - return withKeepAlive ? h(KeepAlive, null, slot()) : slot() - }), - root - ) - return root + const show = ref(true) + const unmount = () => (show.value = false) + const App = { + render() { + return show.value + ? h(BaseTransition, props, () => { + return withKeepAlive ? h(KeepAlive, null, slot()) : slot() + }) + : null + } + } + render(h(App), root) + + return { root, unmount } } function mockProps(extra: BaseTransitionProps = {}, withKeepAlive = false) { @@ -258,7 +265,7 @@ describe('BaseTransition', () => { ) { const toggle = ref(true) const { props, cbs } = mockProps({ mode }) - const root = mount(props, () => + const { root } = mount(props, () => toggle.value ? trueBranch() : falseBranch() ) @@ -347,7 +354,7 @@ describe('BaseTransition', () => { }: ToggleOptions) { const toggle = ref(false) const { props, cbs } = mockProps() - const root = mount(props, () => + const { root } = mount(props, () => toggle.value ? trueBranch() : falseBranch() ) @@ -431,7 +438,7 @@ describe('BaseTransition', () => { ) { const toggle = ref(true) const { props, cbs } = mockProps({}, withKeepAlive) - const root = mount( + const { root } = mount( props, () => (toggle.value ? trueBranch() : falseBranch()), withKeepAlive @@ -537,7 +544,7 @@ describe('BaseTransition', () => { ) { const toggle = ref(true) const { props, cbs } = mockProps({}, withKeepAlive) - const root = mount( + const { root } = mount( props, () => (toggle.value ? trueBranch() : falseBranch()), withKeepAlive @@ -670,7 +677,7 @@ describe('BaseTransition', () => { ) { const toggle = ref(true) const { props, cbs } = mockProps({ mode: 'out-in' }, withKeepAlive) - const root = mount( + const { root } = mount( props, () => (toggle.value ? trueBranch() : falseBranch()), withKeepAlive @@ -763,6 +770,89 @@ describe('BaseTransition', () => { }) }) + + // #6835 + describe('mode: "out-in" toggle again after unmounted', () => { + async function testOutIn( + { + trueBranch, + falseBranch, + trueSerialized, + falseSerialized + }: ToggleOptions, + withKeepAlive = false + ) { + const toggle = ref(true) + const { props, cbs } = mockProps({ mode: 'out-in' }, withKeepAlive) + const { root, unmount } = mount( + props, + () => (toggle.value ? trueBranch() : falseBranch()), + withKeepAlive + ) + + // trigger toggle + toggle.value = false + await nextTick() + // a placeholder is injected until the leave finishes + expect(serializeInner(root)).toBe(`${trueSerialized}`) + expect(props.onBeforeLeave).toHaveBeenCalledTimes(1) + assertCalledWithEl(props.onBeforeLeave, trueSerialized) + expect(props.onLeave).toHaveBeenCalledTimes(1) + assertCalledWithEl(props.onLeave, trueSerialized) + expect(props.onAfterLeave).not.toHaveBeenCalled() + // enter should not have started + expect(props.onBeforeEnter).not.toHaveBeenCalled() + expect(props.onEnter).not.toHaveBeenCalled() + expect(props.onAfterEnter).not.toHaveBeenCalled() + + cbs.doneLeave[trueSerialized]() + expect(props.onAfterLeave).toHaveBeenCalledTimes(1) + assertCalledWithEl(props.onAfterLeave, trueSerialized) + // have to wait for a tick because this triggers an update + await nextTick() + expect(serializeInner(root)).toBe(falseSerialized) + // enter should start + expect(props.onBeforeEnter).toHaveBeenCalledTimes(1) + assertCalledWithEl(props.onBeforeEnter, falseSerialized) + expect(props.onEnter).toHaveBeenCalledTimes(1) + assertCalledWithEl(props.onEnter, falseSerialized) + expect(props.onAfterEnter).not.toHaveBeenCalled() + // finish enter + cbs.doneEnter[falseSerialized]() + expect(props.onAfterEnter).toHaveBeenCalledTimes(1) + assertCalledWithEl(props.onAfterEnter, falseSerialized) + + unmount() + // toggle again after unmounted should not throw error + toggle.value = true + await nextTick() + expect(serializeInner(root)).toBe(``) + + assertCalls(props, { + onBeforeEnter: 1, + onEnter: 1, + onAfterEnter: 1, + onEnterCancelled: 0, + onBeforeLeave: 1, + onLeave: 1, + onAfterLeave: 1, + onLeaveCancelled: 0 + }) + } + + test('w/ elements', async () => { + await runTestWithElements(testOutIn) + }) + + test('w/ components', async () => { + await runTestWithComponents(testOutIn) + }) + + test('w/ KeepAlive', async () => { + await runTestWithKeepAlive(testOutIn) + }) + }) + describe('mode: "out-in" toggle before finish', () => { async function testOutInBeforeFinish( { trueBranch, falseBranch, trueSerialized }: ToggleOptions, @@ -770,7 +860,7 @@ describe('BaseTransition', () => { ) { const toggle = ref(true) const { props, cbs } = mockProps({ mode: 'out-in' }, withKeepAlive) - const root = mount( + const { root } = mount( props, () => (toggle.value ? trueBranch() : falseBranch()), withKeepAlive @@ -847,7 +937,7 @@ describe('BaseTransition', () => { ) { const toggle = ref(true) const { props, cbs } = mockProps({ mode: 'out-in' }, withKeepAlive) - const root = mount( + const { root } = mount( props, () => (toggle.value ? trueBranch() : falseBranch()), withKeepAlive @@ -925,7 +1015,7 @@ describe('BaseTransition', () => { ) { const toggle = ref(true) const { props, cbs } = mockProps({ mode: 'in-out' }, withKeepAlive) - const root = mount( + const { root } = mount( props, () => (toggle.value ? trueBranch() : falseBranch()), withKeepAlive @@ -1029,7 +1119,7 @@ describe('BaseTransition', () => { ) { const toggle = ref(true) const { props, cbs } = mockProps({ mode: 'in-out' }, withKeepAlive) - const root = mount( + const { root } = mount( props, () => (toggle.value ? trueBranch() : falseBranch()), withKeepAlive From bb5e405eed8ed409d86d8b4c361f187e38f65d21 Mon Sep 17 00:00:00 2001 From: Rairn <958414905@qq.com> Date: Mon, 10 Oct 2022 13:49:28 +0800 Subject: [PATCH 3/4] chore: suggestion --- packages/runtime-core/src/components/BaseTransition.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/src/components/BaseTransition.ts b/packages/runtime-core/src/components/BaseTransition.ts index 4b654812ead..affa069c235 100644 --- a/packages/runtime-core/src/components/BaseTransition.ts +++ b/packages/runtime-core/src/components/BaseTransition.ts @@ -239,7 +239,10 @@ const BaseTransitionImpl: ComponentOptions = { leavingHooks.afterLeave = () => { state.isLeaving = false // #6835 - instance.update.active !== false && instance.update() + // it also needs to be updated when active is undefined + if (instance.update.active !== false) { + instance.update() + } } return emptyPlaceholder(child) } else if (mode === 'in-out' && innerChild.type !== Comment) { From db40ef73fca46ffb5883d642c9cbdb834bfebcf9 Mon Sep 17 00:00:00 2001 From: Rairn <958414905@qq.com> Date: Mon, 10 Oct 2022 14:09:34 +0800 Subject: [PATCH 4/4] trigger GitHub actions