How to use vue-router inside a pinia store?
Asked Answered
A

2

6

I'm implementing a auth store(with firebase) and depending on the auth I want to route my user to login/logged page.

I'm basically trying to accomplish this: https://github.com/dannyconnell/vue-composition-api-course/blob/module-23/vue-composition-api-noteballs/src/stores/storeAuth.js

but in typescript.

In my main.ts, I did declare the store as property:

const app = createApp(App);
const pinia = createPinia();
pinia.use(({ store }) => {
  store.router = markRaw(router);
});
app.use(pinia);
app.use(router);
app.mount('#app');

But still, in my store, it doesn't know that I've a router property:

export const useStoreAuth = defineStore('storeAuth', {
    state: () => {
      return {
        user: {},
      } as AuthState;
    },
    actions: {
      init() {
        onAuthStateChanged(auth, user => {
          if (user && user.uid && user.email) {
            this.user = { id: user.uid, email: user.email };
            this.router.push('/'); //--> router doesn't exists
          } else {
            this.user = null;
            this.router.replace('/auth');//--> router doesn't exists
          }
        });
      },
      //...
    }
});

this.router doesn't exist, I get the following error:

Property 'router' does not exist on type '{ init(): void; registerUser(credentials: any): void; loginUser(credentials: any): void; logoutUser(): void; } & { user: { id: string; email: string; } | null; } & _StoreWithState<"storeAuth", AuthState, {}, { ...; }> & _StoreWithGetters<...> & PiniaCustomProperties<...>'.ts(2339)

So how can I make my store aware that the router property exists on the created state?

I've read this but I'm not sure what my "router" is considered, and if it's typed, how to indicate when I create the state which store type I declare?

Amine answered 1/10, 2022 at 9:26 Comment(6)
Please, quote error messages instead of describing them. It's unclear if your problem is with TS types or that this.router physically doesn't exist. Unless you have multiple routers per app, which is rarely the case, you're making it unnecessarily complicated. You could just import a router in a store and use it as isPrecipitin
@EstusFlask I added the error. The course that I was following, and a few links(like #70682167 ) were indicating that the router cannot be imported inside pinia states.Amine
Can you import useRouter from vue-router (like shown here)?Muskrat
@Muskrat This will cause more problems than it solves. useRouter uses Vue composition (provide/inject) and it's suitable to be used in setup function only - or any code that it calls, it'll be unusable in most parts of Pinia storePrecipitin
@Amine This is not true. The problem in the question you linked is that useAuthStore was accessed too early because axiosroot.ts was written in a wrong way. Generally you can just import router (no useRouter) and use it directly in store actions. As for your case, the most simple way to solve it is to add router: null to the state. Or type your plugin propertly, pinia.vuejs.org/core-concepts/plugins.html#typing-plugins . Any way, this.router is useless abstraction herePrecipitin
@EstusFlask Do you mind sharing in an answer how to "type my plugin" ?Amine
R
7

Documentation: https://pinia.vuejs.org/core-concepts/plugins.html#typing-plugins

Here is a complete example:

// main.ts
import { createApp, markRaw } from 'vue';
import { createPinia } from 'pinia';
import type { Router } from 'vue-router';
import App from './App.vue';
import router from './router';

declare module 'pinia' {
  export interface PiniaCustomProperties {
    router: Router;
  }
}

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

pinia.use(({ store }) => {
  store.router = markRaw(router);
});

app.use(pinia);
app.use(router);
app.mount('#app');
Roberge answered 21/11, 2022 at 15:14 Comment(0)
S
-1

You can try this:

import { useRouter } from 'vue-router';

export const useStoreAuth = defineStore('storeAuth', {
    state: () => {
      return {
        user: {},
      } as AuthState;
    },
    actions: {
      init() {
        const router = useRouter(); //--> define router here;
        onAuthStateChanged(auth, user => {
          if (user && user.uid && user.email) {
            this.user = { id: user.uid, email: user.email };
            router.push('/'); //--> this should work
          } else {
            this.user = null;
            router.replace('/auth');//--> this should work
          }
        });
      },
    }
});

It might be a bit inconvenient to define the router in every action if you need to use it, but here's the way I'm using it, you can refer.

Salpiglossis answered 4/10, 2022 at 9:12 Comment(2)
According to @EstusFlask, the useRouter() is meant to be used only in setup method and might not always work? You did not encounter such issues?Amine
Oh that right, mb, i'm using that way with setup methodKaufmann

© 2022 - 2024 — McMap. All rights reserved.