20

I am using pinia and vue-router 4.x ,but i am having a problem using both of them in a store. each works independently ,but not together.

If i use

import router from '../router'

router will work but pinia is failed with error

Uncaught ReferenceError: Cannot access 'useAuthStore' before initialization
at axiosroot.ts

@line let authStore = useAuthStore(pinia);

//here is axiosroot.ts

import axios from "axios";
import {useAuthStore} from '../stores/auth-store'
import { createPinia } from "pinia";
 const pinia=createPinia();
let authStore = useAuthStore(pinia);
const url = "http://127.0.0.1:8000/api";
const   myToken =authStore.getToken;
export default axios.create({
  url,
  headers:{"Accept":"application/json"},
  
});

When i import router from vue-routern useRouter is undefined

import {useRouter} from 'vue-router'
const router =useRouter();

the error

Uncaught TypeError: Cannot read properties of undefined (reading 'push') 
--- 
error @ line router.push({name:'Login'})

// here is the remainning relavant code


import { defineStore, acceptHMRUpdate } from "pinia";
//import router from '../router'
import {useRouter} from 'vue-router'
const router =useRouter();
export const useAuthStore = defineStore({
 id: "user",
 actions: {  
   LogOut(payload) {
     // this.DELETE_TOKEN(null);
     // this.UPDATE_LOGIN_STATUS(false);
     router.push({name:'Login'})
   },
 },
});
SeyT
  • 773
  • 1
  • 10
  • 23

6 Answers6

30

Router has to be used as a plugin in pinia. I read this in the pinia.documentation https://pinia.vuejs.org/core-concepts/plugins.html

When adding external properties, class instances that come from other libraries, or simply things that are not reactive, you should wrap the object with markRaw() before passing it to pinia. Here is an example adding the router to every store:

import { markRaw } from 'vue'
// adapt this based on where your router is
import { router } from './router'

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

This will add pinia to every store you create. The config in main.ts is the same as for vue plugins.

import { createApp, markRaw } from 'vue';
import { createPinia } from 'pinia';
import App from './app/App.vue';
import { router } from './modules/router/router';

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

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

Now you can access router in your stores.

export const useMyStore = defineStore("myStore", {
  state: () => {
    return {};
  },
  actions: {
    myAction() {
      this.$router.push({ name: 'Home' }); 
  },
});

Marv
  • 3,517
  • 2
  • 22
  • 47
Finn Stolze
  • 301
  • 2
  • 3
  • 1
    Any idea how to add/extend Pinia types to accommodate this new property to the store? – Riza Khan Jul 03 '22 at 14:00
  • app.use(pinia) is missing otherwise you'll get: Uncaught Error: []: getActivePinia was called with no active Pinia. Did you forget to install pinia? const pinia = createPinia() app.use(pinia) – ShadowGames Jul 08 '22 at 06:56
  • How do you access such properties when using a setup store though? Where there is no `this`? – Douglas Gaskell Jan 28 '23 at 22:49
  • Thanks for this. I spent several hours trying to figure out why I couldn't call useStore() in a pinia action method. This solved it. It's not exactly intuitive but what about Vuejs really is – Todd Hammer May 10 '23 at 17:10
  • I had to replace the line **store.$router = markRaw(router)** with **store.store.$router = markRaw(router)** to correctly inject $router into the store's state. I'm using Vue 2. – Mike May 26 '23 at 22:36
5

I resolved that wrapping the store in a function:

/*** THIS DOES NOT WORK ****/
const router = useRouter();

export const useLoginStore = defineStore("login", {
  state: () => ({
    loading: false,
    error: "",
  }),
  actions: {
    loginAction(credentials: AuthenticationRequest): Promise<void> {
      await login(credentials);
      router.replace("/")
    },
  },
});



/*** This actually works ***/
export default function useLoginStore() {
  const router = useRouter();

  return defineStore("login", {
    state: () => ({
      loading: false,
      error: "",
    }),
    actions: {
      loginAction(credentials: AuthenticationRequest): Promise<void> {
        await login(credentials);
        router.replace("/")
      },
    },
  })();
}
alexseik
  • 51
  • 2
5

In main.js:

import { createApp, markRaw } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'

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

app.use(pinia)
app.use(router) 
app.mount('#app')

In your store push routes with:

this.router.push({ name: 'home' });
UnuSec
  • 155
  • 1
  • 9
3

For anyone using quasar v2. This worked for me

in your /src/stores/index.js file, add the following

import { markRaw } from 'vue'
import { route } from 'quasar/wrappers'

pinia.use(({ store }) => {
  // important! dont add a $router here
  store.router = markRaw(route)
})

Then in your src/stores/[yourStore].js file use

this.router.push("/") // important! dont add a $router here

I was having trouble when trying to add the $router as suggested in the top answer

  • This is a good solution for quasar. But your answer has has an error. Your `markRaw(router)` shoud be `markRaw(route)`. Tested it and it worked –  Jun 30 '22 at 21:42
  • thnx for that. hv updated the answer. – Ainsof So'o Sep 09 '22 at 02:13
  • In the Quasar documentation it is written that you may include your router into your Quasar app by adding it in `quasar.config.js` throught the option [sourceFiles](https://quasar.dev/quasar-cli-vite/quasar-config-js#sourcefiles). Thereafter, you are able to [use the router from within your store](https://quasar.dev/quasar-cli-vite/state-management-with-pinia#accessing-the-router-in-pinia-stores) – Gillanius Mar 27 '23 at 10:18
1

For anyone using quasar Framework v2

Locate /src/stores/index.js file and edit by

import { useRouter } from 'vue-router' ---> import this

// You can add Pinia plugins here
// pinia.use(SomePiniaPlugin)
pinia.router = useRouter() ---> add this line 

in the Store file

this.router.push({ name: 'Home' }) --> use this
-1

It should work if you declare const router = useRouter(); inside your store action instead of at the top of the module.

It's the same for const store = useSomeStore() declarations. They can't be used at the module route, and are often used within component setup functions and store actions.

You don't need to create a plugin, but if you're likely to be using the router across all your stores it still might be worth doing so as mentioned in this answer

Daniel Storey
  • 825
  • 4
  • 8