Seyid Takele
Seyid Takele

Reputation: 1228

Can't use vue-router and pinia inside a single store

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'})
   },
 },
});

Upvotes: 29

Views: 35086

Answers (9)

Cristian Echartea
Cristian Echartea

Reputation: 1

I am using quasar and resolve the error as follows.

actions: {
    async login(email, password) {
     .....
      await this.api.post('auth/login/', data).then((response) => {
        this._p.$router.push({ name: 'dashboard' });
      });
    },

Something you can do is console.log(this) and you will see everything that your instance contains, in my case it was in "_p". I hope and serve you.

Upvotes: 0

user3094755
user3094755

Reputation: 1641

Here's a pattern which seems to work quite well.

After action initState() you'll have this.route & this.router available

This is for pinia, but would work just as well with vuex.

// any store.js

export const useStore = defineStore('my-store') => {
  return state: () => {
    router: null,
    route: null
  },
  actions: {
    initState ({ router, route }) {
      this.router = router;
      this.route = route;
    }
  }
}
// app.vue

<script setup>

const store = useStore();

const router = useRouter();

const route = useRoute();

store.initState({ router, route })

</script>

Upvotes: 1

Aciddiz
Aciddiz

Reputation: 636

If you need to use router in Pinia without passing methods, props or Object a cleaner solution could be this.

import './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'

import App from './App.vue'
import router from './router'

const pinia = createPinia()

const app = createApp(App)
app.use(pinia)
app.use(router)
app.provide('router', router);
app.mount('#app')

And then in your store:

import { inject } from "vue";
actions: {
    navigate() {
      const router = inject('router');
      router.push({
        name: "customers",
        params: { page: this.currentPage },
      });
    },
},

pinia.use() is more flexible and allows you to pass plugins, methods, Object, props. Both are fine for the porpouse of passing router to pinia.

Upvotes: 0

Ainsof So&#39;o
Ainsof So&#39;o

Reputation: 56

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

Upvotes: 4

Kayode Ibrahim
Kayode Ibrahim

Reputation: 126

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

Upvotes: 2

UnuSec
UnuSec

Reputation: 205

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

Upvotes: 10

Finn Stolze
Finn Stolze

Reputation: 391

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' }); 
  },
});

Upvotes: 39

Daniel Storey
Daniel Storey

Reputation: 943

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

Upvotes: -2

alexseik
alexseik

Reputation: 61

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("/")
      },
    },
  })();
}

Upvotes: 6

Related Questions