Repository: vuejs/vue
Pull request: fix(lifecycle): scope might changed when call hook

<!--
Please make sure to read the Pull Request Guidelines:
https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#pull-request-guidelines
-->

<!-- PULL REQUEST TEMPLATE -->
<!-- (Update "[ ]" to "[x]" to check a box) -->

**What kind of change does this PR introduce?** (check at least one)

- [x] Bugfix
- [ ] Feature
- [ ] Code style update
- [ ] Refactor
- [ ] Build-related changes
- [ ] Other, please describe:

**Does this PR introduce a breaking change?** (check one)

- [ ] Yes
- [x] No

If yes, please describe the impact and migration path for existing applications:

**The PR fulfills these requirements:**

- [x] It's submitted to the `main` branch for v2.x (or to a previous version branch)
- [ ] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number)
- [x] All tests are passing: https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#development-setup
- [x] New/updated tests are included

If adding a **new feature**, the PR's description includes:
- [ ] A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it)

**Other information:**


This pull request's issue originates from a specific problem mentioned in https://github.com/vuejs/pinia/issues/1800 . Due to the potential for callHook to modify the upper-level scope's behavior of collecting effects, it subsequently leads to incorrect effects being torn down when stopping in the wrong scope.


Changed files:
filename: src/core/instance/lifecycle.ts
patch: @@ -18,6 +18,7 @@ import {
   invokeWithErrorHandling
 } from '../util/index'
 import { currentInstance, setCurrentInstance } from 'v3/currentInstance'
+import { getCurrentScope } from 'v3/reactivity/effectScope'
 import { syncSetupProxy } from 'v3/apiSetup'
 
 export let activeInstance: any = null
@@ -398,7 +399,8 @@ export function callHook(
 ) {
   // #7573 disable dep collection when invoking lifecycle hooks
   pushTarget()
-  const prev = currentInstance
+  const prevInst = currentInstance
+  const prevScope = getCurrentScope()
   setContext && setCurrentInstance(vm)
   const handlers = vm.$options[hook]
   const info = `${hook} hook`
@@ -410,6 +412,10 @@ export function callHook(
   if (vm._hasHookEvent) {
     vm.$emit('hook:' + hook)
   }
-  setContext && setCurrentInstance(prev)
+  if (setContext) {
+    setCurrentInstance(prevInst)
+    prevScope && prevScope.on()
+  }
+
   popTarget()
 }

filename: test/unit/features/v3/reactivity/effectScope.spec.ts
patch: @@ -1,3 +1,4 @@
+import Vue from 'vue'
 import { nextTick } from 'core/util'
 import {
   watch,
@@ -290,4 +291,28 @@ describe('reactivity/effectScope', () => {
       expect(getCurrentScope()).toBe(parentScope)
     })
   })
+
+  it('scope should not break currentScope when component call hooks', () => {
+    const scope = new EffectScope()
+    const vm = new Vue({
+      template: `
+          <div>
+            <div v-if="show" />
+          </div>
+        `,
+      data() {
+        return {
+          show: false
+        }
+      }
+    }).$mount()
+
+    scope.run(() => {
+      // call renderTriggered hook here
+      vm.show = true
+      // this effect should be collected by scope not the component scope
+      effect(() => {})
+    })
+    expect(scope.effects.length).toBe(1)
+  })
 })