简体   繁体   中英

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

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

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

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(router)
})

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

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

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

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

It's very easy to use vue-router inside of pinia store if you write your pinia store like a composable function. These setup stores are easy to write if you are already familiar with the composition API.


  
  
        
            import {
              useRouter
            } from "vue-router";
            import {
              ref,
              computed
            } from "vue"
            import {
              defineStore
            } from "pinia";
        
            export const useAuthStore = defineStore('authStore', () => {
              const router = useRouter()
        
              // your states are just reactive variables
              const token = ref(localStorage.getItem('token'))
        
              // your getters are just computed properties
              const loginStatus = computed(() => {
                return token.value ? true : false
              })
        
              // your actions are just javascript functions
              const DELETE_TOKEN(payload) = () => {
                token.value = payload
              }
        
              const UPDATE_LOGIN_STATUS = (payload) => {
                loginStatus.value = payload;
              }
        
              const logOut(payload) {
                DELETE_TOKEN(null);
                UPDATE_LOGIN_STATUS(false);
                router.push({
                  name: 'Login'
                })
              }
        
              return {
                token,
                loginStatus,
                DELETE_TOKEN,
                UPDATE_LOGIN_STATUS,
                logOut
              }
        
            })

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM