Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nested calls to runWithContext resets the app for the rest of the top runWithContext call #10260

Closed
justinrlle opened this issue Feb 2, 2024 · 0 comments · Fixed by #10261
Closed

Comments

@justinrlle
Copy link

justinrlle commented Feb 2, 2024

Vue version

3.4.15

Link to minimal reproduction

https://stackblitz.com/edit/vue3-nested-runwithcontext?file=src%2Fmain.js

Steps to reproduce

  1. Call app.runWithContext
  2. In the callback, use inject. It works as expected
  3. In the callback, call again app.runWithContext
  4. In the nested callback, use again inject. It works again as expected
  5. 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:
Screenshot 2024-02-02 at 16 08 47

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.10

Any 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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant