-
-
Notifications
You must be signed in to change notification settings - Fork 8.9k
Description
Vue version
3.4.15
Link to minimal reproduction
https://stackblitz.com/edit/vue3-nested-runwithcontext?file=src%2Fmain.js
Steps to reproduce
- Call
app.runWithContext - In the callback, use
inject. It works as expected - In the callback, call again
app.runWithContext - In the nested callback, use again
inject. It works again as expected - Back to the first callback. use a third time
inject. It doesn't work anymore
A situation where one can encounter such issue is in a combination between vue-router guards and pinia stores:
// simplified code from where I've encountered the issue
router.beforeEach(() => { // this is the top runWithContext()
const store = useSomePiniaStore(); // this is the nested runWithContext
const injected = inject('some-global'); // this doesn't work
});To simply reproduce it on any app (with vue > 3.3), run the following:
app.provide('global', { global: 'provided' });
app.runWithContext(() => {
console.log('first call', inject('global'));
app.runWithContext(() => {
console.log('nested call', inject('global'));
});
console.log('last call', inject('global'));
});You'll see the following logs:

What is expected?
Calling inject or other API expecting a scope should be working for the whole duration of the app.runWithContext().
What is actually happening?
When the nested app.runWithContext() call finishes, the currentApp variable is reset to null, rendering it empty for the rest of the app.runWithContext.
System Info
System:
OS: macOS 14.0
CPU: (8) arm64 Apple M1
Memory: 100.55 MB / 16.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 18.12.1 - ~/.local/share/volta/tools/image/node/18.12.1/bin/node
Yarn: 1.22.19 - ~/.local/share/volta/tools/image/yarn/1.22.19/bin/yarn
npm: 8.19.2 - ~/.local/share/volta/tools/image/node/18.12.1/bin/npm
bun: 1.0.23 - /opt/homebrew/bin/bun
Browsers:
Chrome: 121.0.6167.139
Safari: 17.0
npmPackages:
vue: ^3.4.9 => 3.4.10Any additional comments?
In my case, I can just move the call to inject to be the first line of the router guard, and this fixes the issue, but just because I can do it this way.
I think using a sort of stack like for the EffectScope would work, so replacing the current:
runWithContext(fn) {
currentApp = app
try {
return fn()
} finally {
currentApp = null
}
}With:
runWithContext(fn) {
let previousApp = currentApp;
try {
currentApp = app
return fn()
} finally {
currentApp = previousApp
}
}should work, at least it did fix my test case. Not sure how that would work with async, but runWithContext looks to only support synchronous calling, so that may be fine.