vue.jsvue-routervuejs3pinia

Vue: Can't access Pinia Store in beforeEnter vue-router


I am using Vue 3 including the Composition API and additionally Pinia as State Management.

In the options API there is a method beforeRouteEnter, which is built into the component itself. Unfortunately this method does not exist in the composition API. Here the code, which would have been in the beforeRouteEnter method, is written directly into the setup method. However, this means that the component is loaded and displayed first, then the code is executed and, if the check fails, the component is redirected to an error page, for example.

My idea was to make my check directly in the route configuration in the beforeEnter method of a route. However, I don't have access to the Pinia Store, which doesn't seem to be initialized yet, although it is called before in the main.js.

Console Log

Uncaught Error: [🍍]: getActivePinia was called with no active Pinia. Did you forget to install pinia?
    const pinia = createPinia()
    app.use(pinia)
This will fail in production.

Router.js

import { useProcessStore } from "@/store/process";

const routes: Array<RouteRecordRaw> = [
{
    path: "/processes/:id",
    name: "ProcessView",
    component: loadView("ProcessView", "processes/"),
    beforeEnter: () => {
      const processStore = useProcessStore();
      console.log(processStore);
    },
    children: [
      {
        path: "steer",
        name: "ProcessSteer",
        component: loadView("ProcessSteer", "processes/")
      },
      {
        path: "approve/:code",
        name: "ProcessApprove",
        component: loadView("ProcessApprove", "processes/")
      }
    ]
  },
];

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
});

export default router;

main.js

import { createApp } from "vue";
import "@/assets/bundle-bootstrap.css";
import App from "@/App.vue";
import { createPinia } from "pinia";
import router from "@/router";
import SvgIcon from "@/components/SvgIcon.vue";

const pinia = createPinia();
const app = createApp(App);

app.use(pinia);
app.use(router);
app.component("SvgIcon", SvgIcon);

router.isReady().then(() => {
  app.mount("#app");
});


Solution

  • However, I don't have access to the Pinia Store, which doesn't seem to be initialized yet, although it is called before in the main.js

    Before what? Pinia instance is created with const pinia = createPinia(); after the router module is imported - while it is imported, all side-effects including the call to createRouter() are executed. Once the router is created it begins it's initial navigation (on client - on server you need to trigger it with router.push()) - if you happen to be at URL matching the route with guard that is using Pinia store, the useProcessStore() happens before Pinia is created...

    Using a store outside of a component

    You have two options:

    // store.js
    import { createPinia } from "pinia";
    
    const pinia = createPinia();
    
    export default pinia;
    
    // router.js
    import pinia from "@/store.js";
    import { useProcessStore } from "@/store/process";
    
    const routes: Array<RouteRecordRaw> = [
    {
        path: "/processes/:id",
        name: "ProcessView",
        component: loadView("ProcessView", "processes/"),
        beforeEnter: () => {
          const processStore = useProcessStore(pinia ); // <-- passing Pinia instance directly
          console.log(processStore);
        },
      },
    ];
    
    const router = createRouter({
      history: createWebHistory(process.env.BASE_URL),
      routes
    });
    
    export default router;
    
    // main.js
    import { createApp } from "vue";
    import App from "@/App.vue";
    import store from "@/store.js";
    import router from "@/router";
    
    const app = createApp(App);
    
    app.use(store);
    app.use(router);
    
    router.isReady().then(() => {
      app.mount("#app");
    });