Skip to content

Commit

Permalink
fix(runtime-core): do not fire mount/activated hooks if unmounted bef…
Browse files Browse the repository at this point in the history
…ore mounted (#9370)

close #8898
close #9264
close #9617
  • Loading branch information
edison1105 authored Jun 7, 2024
1 parent 32262a9 commit aa156ed
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 10 deletions.
58 changes: 58 additions & 0 deletions packages/runtime-core/__tests__/apiLifecycle.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {
KeepAlive,
TrackOpTypes,
h,
nextTick,
nodeOps,
onActivated,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
Expand Down Expand Up @@ -407,4 +409,60 @@ describe('api: lifecycle hooks', () => {
await nextTick()
expect(fn).toHaveBeenCalledTimes(4)
})

it('immediately trigger unmount during rendering', async () => {
const fn = vi.fn()
const toggle = ref(false)

const Child = {
setup() {
onMounted(fn)
// trigger unmount immediately
toggle.value = false
return () => h('div')
},
}

const Comp = {
setup() {
return () => (toggle.value ? [h(Child)] : null)
},
}

render(h(Comp), nodeOps.createElement('div'))

toggle.value = true
await nextTick()
expect(fn).toHaveBeenCalledTimes(0)
})

it('immediately trigger unmount during rendering(with KeepAlive)', async () => {
const mountedSpy = vi.fn()
const activeSpy = vi.fn()
const toggle = ref(false)

const Child = {
setup() {
onMounted(mountedSpy)
onActivated(activeSpy)

// trigger unmount immediately
toggle.value = false
return () => h('div')
},
}

const Comp = {
setup() {
return () => h(KeepAlive, [toggle.value ? h(Child) : null])
},
}

render(h(Comp), nodeOps.createElement('div'))

toggle.value = true
await nextTick()
expect(mountedSpy).toHaveBeenCalledTimes(0)
expect(activeSpy).toHaveBeenCalledTimes(0)
})
})
3 changes: 0 additions & 3 deletions packages/runtime-core/src/apiLifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ export function injectHook(
const wrappedHook =
hook.__weh ||
(hook.__weh = (...args: unknown[]) => {
if (target.isUnmounted) {
return
}
// disable tracking inside all lifecycle hooks
// since they can potentially be called inside effects.
pauseTracking()
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime-core/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ export type Component<

export type { ComponentOptions }

type LifecycleHook<TFn = Function> = TFn[] | null
export type LifecycleHook<TFn = Function> = (TFn & SchedulerJob)[] | null

// use `E extends any` to force evaluating type to fix #2362
export type SetupContext<
Expand Down
4 changes: 4 additions & 0 deletions packages/runtime-core/src/components/KeepAlive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
type RendererElement,
type RendererInternals,
type RendererNode,
invalidateMount,
queuePostRenderEffect,
} from '../renderer'
import { setTransitionHooks } from './BaseTransition'
Expand Down Expand Up @@ -166,6 +167,9 @@ const KeepAliveImpl: ComponentOptions = {

sharedContext.deactivate = (vnode: VNode) => {
const instance = vnode.component!
invalidateMount(instance.m)
invalidateMount(instance.a)

move(vnode, storageContainer, null, MoveType.LEAVE, parentSuspense)
queuePostRenderEffect(() => {
if (instance.da) {
Expand Down
11 changes: 10 additions & 1 deletion packages/runtime-core/src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
type ComponentInternalInstance,
type ComponentOptions,
type Data,
type LifecycleHook,
createComponentInstance,
setupComponent,
} from './component'
Expand Down Expand Up @@ -2266,7 +2267,9 @@ function baseCreateRenderer(
unregisterHMR(instance)
}

const { bum, scope, update, subTree, um } = instance
const { bum, scope, update, subTree, um, m, a } = instance
invalidateMount(m)
invalidateMount(a)

// beforeUnmount hook
if (bum) {
Expand Down Expand Up @@ -2533,3 +2536,9 @@ function locateNonHydratedAsyncRoot(
}
}
}

export function invalidateMount(hooks: LifecycleHook) {
if (hooks) {
for (let i = 0; i < hooks.length; i++) hooks[i].active = false
}
}
8 changes: 3 additions & 5 deletions packages/runtime-core/src/scheduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,11 @@ export function flushPostFlushCbs(seen?: CountMap) {
postFlushIndex < activePostFlushCbs.length;
postFlushIndex++
) {
if (
__DEV__ &&
checkRecursiveUpdates(seen!, activePostFlushCbs[postFlushIndex])
) {
const cb = activePostFlushCbs[postFlushIndex]
if (__DEV__ && checkRecursiveUpdates(seen!, cb)) {
continue
}
activePostFlushCbs[postFlushIndex]()
if (cb.active !== false) cb()
}
activePostFlushCbs = null
postFlushIndex = 0
Expand Down

0 comments on commit aa156ed

Please sign in to comment.