I can't access my routes from the store. There may be a good explanation for this. I use Vuejs3 and Pinia
My store :
import {defineStore} from 'pinia'
import {useRoute} from "vue-router";
type navigationState = {
selectedNavigationItem: INavigationItem | null,
selectedNavigationPage: INavigationPage | null,
}
export const useNavigationStore = defineStore('navigationStore', {
state: () => ({
/**
* when the user clicks on an element of the navbar we store the navigation item here
*/
selectedNavigationItem: null,
/**
* when the user clicks on an element of the sidebar we store the navigation page here
*/
selectedNavigationPage: null,
} as navigationState),
actions: {
/**
* Set Selected navigation page
* @param navigationPage
* @type INavigationPage
*/
setSelectedNavigationPage(navigationPage: INavigationPage | null) {
console.log(useRoute())
this.selectedNavigationPage = navigationPage
},
},
})
when I do a console log like in the method setSelectedNavigationPage
I have an undefined
I can't access my routes from the store. There may be a good explanation for this. I use Vuejs3 and Pinia
My store :
import {defineStore} from 'pinia'
import {useRoute} from "vue-router";
type navigationState = {
selectedNavigationItem: INavigationItem | null,
selectedNavigationPage: INavigationPage | null,
}
export const useNavigationStore = defineStore('navigationStore', {
state: () => ({
/**
* when the user clicks on an element of the navbar we store the navigation item here
*/
selectedNavigationItem: null,
/**
* when the user clicks on an element of the sidebar we store the navigation page here
*/
selectedNavigationPage: null,
} as navigationState),
actions: {
/**
* Set Selected navigation page
* @param navigationPage
* @type INavigationPage
*/
setSelectedNavigationPage(navigationPage: INavigationPage | null) {
console.log(useRoute())
this.selectedNavigationPage = navigationPage
},
},
})
when I do a console log like in the method setSelectedNavigationPage
I have an undefined
Share Improve this question edited Feb 27, 2022 at 9:46 Roland 27.8k9 gold badges106 silver badges103 bronze badges asked Feb 24, 2022 at 9:11 MysterdevMysterdev 2593 silver badges9 bronze badges4 Answers
Reset to default 7EDIT: Thanks for sophiews for pointing this out.
Just found out that we have different way to defineStore: Setup Stores
// src/stores/user.js
import { defineStore } from 'pinia'
import { useRoute, useRouter } from 'vue-router'
import api from './api.js'
export const useUserStore = defineStore('User', () => { // use function
const route = useRoute()
const router = useRouter()
const login = async () => {
await api.POST('login', {username, password})
router.replace({name: 'home'})
}
return { login } // IMPORTANT: need to return anything we need to expose
})
Old answer
You can add router as Pinia plugin
// src/main.js
import { createPinia } from 'pinia'
import { createApp, markRaw } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import App from './App.vue'
import Home from './views/HomePage.vue'
import Api from './api.js' // my axios wrapper
const app = createApp(App)
// I usually put this in a separate file src/router.js and export the router
const routes = [
{ path: '/', ponent: HomePage },
]
const router = createRouter({
history: createWebHistory(),
routes,
})
const pinia = createPinia()
pinia.use(({ store }) => {
store.router = markRaw(router)
store.api = markRaw(Api)
})
app
.use(pinia)
.use(router)
.mount('#app')
Then router
and api
are available on this
// src/stores/user.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('User', {
state: () => ({}),
actions: {
async login() {
await this.api.POST('login', {username, password})
this.router.replace({name: 'home'})
}
}
})
Note that you can't call this.router
with arrow function.
login: async () => {
this.router.replace({name: 'home'}) // error
}
For typescript user, to correctly get type for this.router
and this.api
:
// src/global.d.ts
import { Router } from 'vue-router'
import Api from './api'
export { }
declare global {
}
declare module 'pinia' {
export interface PiniaCustomProperties {
router: Router,
api: typeof Api
}
}
I found this way on pinia github. https://github./vuejs/pinia/discussions/1092
But I still don't know how to add this.route
to Pinia.
Future reader, please ment if you know how to do it.
useRoute
and useRouter
must be used in Vue ponents and specifically setup
method or inside script setup
.
useRouter Docs
useRoute Docs
If you want to access the router
though, you can simply import it:
router-file
import { createRouter, createWebHistory } from 'vue-router'
export const router = createRouter({
history: createWebHistory(),
routes: [/* ... */]
})
then in your pinia store you can import and use the router from that file:
import { defineStore } from 'pinia'
import router from './router'
export const myStore = defineStore('myStore', () => {
// router.push
// router.replace
})
You could wrap the process of instantiating a store within a factory/function, this will allow you to expand the stores capabilities regarding your custom needs. Below you can see that we can instantiate a store referencing the urql client and the router object. Have a look:
export class StoreManager {
static _instances: any[] = [];
public static spawnInstance(
id: string,
storeType?: EStoreType,
clientHandle?: ClientHandle,
routerHandle?: Router,
) {
if (StoreManager._instances.find((i) => i.id === id)) {
const store = StoreManager._instances.find((i) => i.id === id).instance;
return store;
} else {
const store = StoreManager.initStore(
id,
storeType,
clientHandle ?? null,
routerHandle ?? null,
);
StoreManager._instances.push({
id: id,
instance: store,
storeType: storeType,
});
return store;
}
}
public static initStore(
id: string,
storeType: EStoreType,
clientHandle: ClientHandle | null,
routerHandle: Router | null,
) {
const baseState = {
_meta: {
storeType: storeType,
isLoading: true,
},
_client: clientHandle,
_router: routerHandle,
};
const baseActions = {
async query(query: any, variables: any[] = []) {
// use urql client
},
};
const baseGetters = {
storeType: (state) => state._meta.storeType,
getCurrentRoute: (state) => {
if (!state._router) {
throw new RouterNotSetException(
`This store does not have a router set up`,
);
}
return state._router.currentRoute.fullPath.replace('/', '');
},
};
switch (storeType) {
case EStoreType.DEFAULT:
return defineStore({
id: `${id}`,
state: () => ({
...baseState,
}),
actions: {
...baseActions,
},
getters: {
...baseGetters,
},
});
default:
throw new StoreTypeNotFoundException(
`Expected valid 'EStoreType', got ${storeType}`,
);
}
}
}
Within your VueComponent a store instance would be spawned like this:
const store = StoreManager.spawnInstance(
uuidv4(),
EStoreType.DEFAULT,
useClientHandle(),
useRouter(),
)();
Answering how to access the route
in your pinia store if it's in Option Store syntax since it wasn't really answered and @ChristhoferNatalius specifically asked for an answer.
It seems like you can add the route as a plugin to the pinia store in much the same way you would add the router as a plugin.
// main.js
import { useRoute } from 'vue-router'
const pinia = createPinia();
pinia.use(({ store }) => {
store.route = useRoute();
})
app.use(pinia);
// inside your Options Store...
const store = defineStore('myStore', {
state: () => ({ ... })
actions: {
mapUrlQueryParamsToFilters() {
console.log('route', this.route); // entire route object is here
}
}
})
I'm not sure if it's fully correct but i seem to be able to access the route object just fine... I created a thread on Pinia's Github Discussion so people can tell me if i'm wrong lol...
https://github./vuejs/pinia/discussions/2732