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

beforeRouteEnter does not have an injection context if component is not yet loaded #2051

Closed
rijenkii opened this issue Nov 22, 2023 · 2 comments · Fixed by #2117
Closed

beforeRouteEnter does not have an injection context if component is not yet loaded #2051

rijenkii opened this issue Nov 22, 2023 · 2 comments · Fixed by #2117
Labels
bug Something isn't working has workaround A workaround has been found to deal with the issue

Comments

@rijenkii
Copy link

rijenkii commented Nov 22, 2023

Reproduction

https://jsfiddle.net/rv3p5ajn/13/

Code, if JSFiddle is down
<div id="app">
  <pre>url: {{ $route.fullPath }}</pre>
  <ul>
    <li><router-link to="/page1">Page 1</router-link></li>
    <li><router-link to="/page2">Page 2</router-link></li>
  </ul>
  <router-view></router-view>
</div>

<script type="importmap">
  {
    "imports": {
      "vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js",
      "vue-router": "https://unpkg.com/vue-router@4/dist/vue-router.esm-browser.js",
      "@vue/devtools-api": "https://unpkg.com/@vue/devtools-api@6/lib/esm/index.js"
    }
  }
</script>

<script type="module">
import { createApp, hasInjectionContext } from "vue";
import { createRouter, createWebHistory } from "vue-router";

const Page1 = {
  template: `<div>page 1</div>`,
  beforeRouteEnter: () => console.log(hasInjectionContext()),
};
const Page2 = {
  template: `<div>page 2</div>`,
  beforeRouteEnter: () => console.log(hasInjectionContext()),
};

const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: "/page1", component: async () => Page1 },
    { path: "/page2", component: async () => Page2 },
  ],
});

const app = createApp({});
app.use(router);

window.vm = app.mount("#app");
</script>

Steps to reproduce the bug

  1. Open JSFiddle console
  2. Click "Page 1"
  3. Click "Page 2"
  4. Click "Page 1" again
  5. Click "Page 2" again

Expected behavior

After all clicks value of hasInjectionContext() should be true.

Actual behavior

On first and second clicks hasInjectionContext() is false. On third and fourth, when promises are already resolved, hasInjectionContext() is true.

Additional information

According to the docs, all guards should have an injection context.

All other guards do have an injection context, but only before the first yield.

I think the problem lies somewhere within runWithContext, because it behaves weirdly with promises:

const { createApp, hasInjectionContext } = Vue;
const app = createApp({});

app.runWithContext(async () => {
  console.log(hasInjectionContext());
  await new Promise((r) => setTimeout(r));
  console.log(hasInjectionContext());
});

Output is true, then false -- context is reset after first yield.
Maybe the bug is in vuejs/core?

EDIT: Turns out that the weird behaviour of runWithContext with async functions is known and kind of documented in Nuxt's docs: https://nuxt.com/docs/api/composables/use-nuxt-app#runwithcontext

@posva posva added the bug Something isn't working label Nov 22, 2023 — with Volta.net
Copy link
Member

posva commented Nov 22, 2023

It seems that because of lazy loaded routes, the actual guard is not executed with the context anymore. Once the component is lazy loaded it's no longer loaded again, that's why it only happens with beforeRouteEnter.

Copy link
Member

posva commented Nov 22, 2023

I created #2053 but it still needs tests. Feel free to add them. As a workaround you can force a redirect if the injectioncontext is not there:

{
    template: `<div>page 1</div>`,
    beforeRouteEnter: (to) => {
		if (!hasInjectionContext()) {
    	return to.fullPath
    }
    console.log(hasInjectionContext())
    },
  }

@posva posva added contribution welcome The team would welcome a contribution from the community for this issue has workaround A workaround has been found to deal with the issue and removed contribution welcome The team would welcome a contribution from the community for this issue labels Nov 22, 2023 — with Volta.net
yozi-developer added a commit to yozi-developer/vue-router that referenced this issue Jan 26, 2024
@posva posva closed this as completed in 6a69696 Jan 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working has workaround A workaround has been found to deal with the issue
Projects
None yet
2 participants