mirror of https://github.com/vuejs/core.git
fix(transition/keep-alive): fix unmount bug for component with out-in transition (#6839)
fix #6835
This commit is contained in:
parent
48876182db
commit
64e6d9221d
|
@ -19,13 +19,20 @@ function mount(
|
||||||
withKeepAlive = false
|
withKeepAlive = false
|
||||||
) {
|
) {
|
||||||
const root = nodeOps.createElement('div')
|
const root = nodeOps.createElement('div')
|
||||||
render(
|
const show = ref(true)
|
||||||
h(BaseTransition, props, () => {
|
const unmount = () => (show.value = false)
|
||||||
return withKeepAlive ? h(KeepAlive, null, slot()) : slot()
|
const App = {
|
||||||
}),
|
render() {
|
||||||
root
|
return show.value
|
||||||
)
|
? h(BaseTransition, props, () => {
|
||||||
return root
|
return withKeepAlive ? h(KeepAlive, null, slot()) : slot()
|
||||||
|
})
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render(h(App), root)
|
||||||
|
|
||||||
|
return { root, unmount }
|
||||||
}
|
}
|
||||||
|
|
||||||
function mockProps(extra: BaseTransitionProps = {}, withKeepAlive = false) {
|
function mockProps(extra: BaseTransitionProps = {}, withKeepAlive = false) {
|
||||||
|
@ -258,7 +265,7 @@ describe('BaseTransition', () => {
|
||||||
) {
|
) {
|
||||||
const toggle = ref(true)
|
const toggle = ref(true)
|
||||||
const { props, cbs } = mockProps({ mode })
|
const { props, cbs } = mockProps({ mode })
|
||||||
const root = mount(props, () =>
|
const { root } = mount(props, () =>
|
||||||
toggle.value ? trueBranch() : falseBranch()
|
toggle.value ? trueBranch() : falseBranch()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -347,7 +354,7 @@ describe('BaseTransition', () => {
|
||||||
}: ToggleOptions) {
|
}: ToggleOptions) {
|
||||||
const toggle = ref(false)
|
const toggle = ref(false)
|
||||||
const { props, cbs } = mockProps()
|
const { props, cbs } = mockProps()
|
||||||
const root = mount(props, () =>
|
const { root } = mount(props, () =>
|
||||||
toggle.value ? trueBranch() : falseBranch()
|
toggle.value ? trueBranch() : falseBranch()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -431,7 +438,7 @@ describe('BaseTransition', () => {
|
||||||
) {
|
) {
|
||||||
const toggle = ref(true)
|
const toggle = ref(true)
|
||||||
const { props, cbs } = mockProps({}, withKeepAlive)
|
const { props, cbs } = mockProps({}, withKeepAlive)
|
||||||
const root = mount(
|
const { root } = mount(
|
||||||
props,
|
props,
|
||||||
() => (toggle.value ? trueBranch() : falseBranch()),
|
() => (toggle.value ? trueBranch() : falseBranch()),
|
||||||
withKeepAlive
|
withKeepAlive
|
||||||
|
@ -537,7 +544,7 @@ describe('BaseTransition', () => {
|
||||||
) {
|
) {
|
||||||
const toggle = ref(true)
|
const toggle = ref(true)
|
||||||
const { props, cbs } = mockProps({}, withKeepAlive)
|
const { props, cbs } = mockProps({}, withKeepAlive)
|
||||||
const root = mount(
|
const { root } = mount(
|
||||||
props,
|
props,
|
||||||
() => (toggle.value ? trueBranch() : falseBranch()),
|
() => (toggle.value ? trueBranch() : falseBranch()),
|
||||||
withKeepAlive
|
withKeepAlive
|
||||||
|
@ -670,7 +677,7 @@ describe('BaseTransition', () => {
|
||||||
) {
|
) {
|
||||||
const toggle = ref(true)
|
const toggle = ref(true)
|
||||||
const { props, cbs } = mockProps({ mode: 'out-in' }, withKeepAlive)
|
const { props, cbs } = mockProps({ mode: 'out-in' }, withKeepAlive)
|
||||||
const root = mount(
|
const { root } = mount(
|
||||||
props,
|
props,
|
||||||
() => (toggle.value ? trueBranch() : falseBranch()),
|
() => (toggle.value ? trueBranch() : falseBranch()),
|
||||||
withKeepAlive
|
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', () => {
|
describe('mode: "out-in" toggle before finish', () => {
|
||||||
async function testOutInBeforeFinish(
|
async function testOutInBeforeFinish(
|
||||||
{ trueBranch, falseBranch, trueSerialized }: ToggleOptions,
|
{ trueBranch, falseBranch, trueSerialized }: ToggleOptions,
|
||||||
|
@ -770,7 +860,7 @@ describe('BaseTransition', () => {
|
||||||
) {
|
) {
|
||||||
const toggle = ref(true)
|
const toggle = ref(true)
|
||||||
const { props, cbs } = mockProps({ mode: 'out-in' }, withKeepAlive)
|
const { props, cbs } = mockProps({ mode: 'out-in' }, withKeepAlive)
|
||||||
const root = mount(
|
const { root } = mount(
|
||||||
props,
|
props,
|
||||||
() => (toggle.value ? trueBranch() : falseBranch()),
|
() => (toggle.value ? trueBranch() : falseBranch()),
|
||||||
withKeepAlive
|
withKeepAlive
|
||||||
|
@ -847,7 +937,7 @@ describe('BaseTransition', () => {
|
||||||
) {
|
) {
|
||||||
const toggle = ref(true)
|
const toggle = ref(true)
|
||||||
const { props, cbs } = mockProps({ mode: 'out-in' }, withKeepAlive)
|
const { props, cbs } = mockProps({ mode: 'out-in' }, withKeepAlive)
|
||||||
const root = mount(
|
const { root } = mount(
|
||||||
props,
|
props,
|
||||||
() => (toggle.value ? trueBranch() : falseBranch()),
|
() => (toggle.value ? trueBranch() : falseBranch()),
|
||||||
withKeepAlive
|
withKeepAlive
|
||||||
|
@ -925,7 +1015,7 @@ describe('BaseTransition', () => {
|
||||||
) {
|
) {
|
||||||
const toggle = ref(true)
|
const toggle = ref(true)
|
||||||
const { props, cbs } = mockProps({ mode: 'in-out' }, withKeepAlive)
|
const { props, cbs } = mockProps({ mode: 'in-out' }, withKeepAlive)
|
||||||
const root = mount(
|
const { root } = mount(
|
||||||
props,
|
props,
|
||||||
() => (toggle.value ? trueBranch() : falseBranch()),
|
() => (toggle.value ? trueBranch() : falseBranch()),
|
||||||
withKeepAlive
|
withKeepAlive
|
||||||
|
@ -1029,7 +1119,7 @@ describe('BaseTransition', () => {
|
||||||
) {
|
) {
|
||||||
const toggle = ref(true)
|
const toggle = ref(true)
|
||||||
const { props, cbs } = mockProps({ mode: 'in-out' }, withKeepAlive)
|
const { props, cbs } = mockProps({ mode: 'in-out' }, withKeepAlive)
|
||||||
const root = mount(
|
const { root } = mount(
|
||||||
props,
|
props,
|
||||||
() => (toggle.value ? trueBranch() : falseBranch()),
|
() => (toggle.value ? trueBranch() : falseBranch()),
|
||||||
withKeepAlive
|
withKeepAlive
|
||||||
|
|
|
@ -238,7 +238,11 @@ const BaseTransitionImpl: ComponentOptions = {
|
||||||
// return placeholder node and queue update when leave finishes
|
// return placeholder node and queue update when leave finishes
|
||||||
leavingHooks.afterLeave = () => {
|
leavingHooks.afterLeave = () => {
|
||||||
state.isLeaving = false
|
state.isLeaving = false
|
||||||
instance.update()
|
// #6835
|
||||||
|
// it also needs to be updated when active is undefined
|
||||||
|
if (instance.update.active !== false) {
|
||||||
|
instance.update()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return emptyPlaceholder(child)
|
return emptyPlaceholder(child)
|
||||||
} else if (mode === 'in-out' && innerChild.type !== Comment) {
|
} else if (mode === 'in-out' && innerChild.type !== Comment) {
|
||||||
|
|
Loading…
Reference in New Issue