3

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?

J4N
  • 19,480
  • 39
  • 187
  • 340
  • 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 is – Estus Flask Oct 01 '22 at 09:38
  • @EstusFlask I added the error. The course that I was following, and a few links(like https://stackoverflow.com/questions/70681667/cant-use-vue-router-and-pinia-inside-asingle-a-store ) were indicating that the router cannot be imported inside pinia states. – J4N Oct 01 '22 at 10:16
  • Can you import `useRouter` from `vue-router` (like shown [here](https://stackoverflow.com/a/68583537/13013715))? – STh Oct 01 '22 at 10:35
  • @STh 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 store – Estus Flask Oct 01 '22 at 10:38
  • @J4N 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, https://pinia.vuejs.org/core-concepts/plugins.html#typing-plugins . Any way, `this.router` is useless abstraction here – Estus Flask Oct 01 '22 at 10:42
  • @EstusFlask Do you mind sharing in an answer how to "type my plugin" ? – J4N Oct 01 '22 at 11:45

2 Answers2

5

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');
0

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.

Quốc Cường
  • 417
  • 4
  • 12