Vue3 and Pinia: store state is undefined in other stores - vue.js

I want to get users in threadStore from userStore, but users is undefined. And I have no idea why.
Also in userStore threads from threadStore has a correct value.
Maybe that because threadStore installed first than userStore?
But how I can get users from userStore in threadStore?
log image
thread.js
import { defineStore } from 'pinia';
import { ref } from 'vue';
import { useUserStore } from './user';
export const useThreadStore = defineStore('thread', () => {
const userStore = useUserStore();
console.log(userStore.users); // is undefined
const threads = ref([]);
return {
threads,
}
user.js
import { defineStore } from 'pinia';
import { ref } from 'vue';
import { useThreadStore } from './thread';
export const useUserStore = defineStore('user', () => {
const threadStore = useThreadStore();
console.log(threadStore.threads); // has a correct value
const users = ref([]);
return {
users,
};
});

Related

how to use pinia store in a vue router navigation guard?

I'm using Vuejs 3, vuerouter 4 and pinia and trying to put a navigation guard in some routes, as per the example in the documentation (if a user is not authenticated and is not on the login page, send the user to login page to get authenticated). This is explained also in pinia documentation on use of pinia outside of components. But I can't get my head around it.
The store I use is currently simple (return false or true on isAuthenticated):
//authStore.js
import { defineStore } from "pinia";
export const useAuthStore = defineStore( 'AuthStore', {
state: () => {
return {
isAuthenticated: false
}
}
})
I want to use this isAuthenticated in a beforeEnter in routes/index.js
In main.js:
import { useAuthStore } from '#/stores/authStore'
import { createApp } from 'vue'
import App from './App.vue'
import router from '#/router'
import { createPinia } from 'pinia'
const app = createApp(App)
app.use(createPinia()).use(router)
app.mount('#app')
// I'm not using authStore in this file, so this raises an error:
const authStore = useAuthStore()
And in router/index.js:
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
// path for login page,
{
path: '/adm/home',
name: 'AdmView',
component: () => import('#/views/adm/AdmView.vue'),
beforeEnter: (to) => {
// error: authStore is not defined
const isAuthenticated = authStore
if ( !isAuthenticated && to.name !== 'login-adm' ) {
return { name: 'login-adm' }
}
}
},
// other paths
]
Your authStore.js exports useAuthStore as expected, but you do not call it as required.
authStore is not defined because authStore is the filename of your auth store -- instead you should be executing the function useAuthStore exported from that file:
const authStore = useAuthStore();
console.log('Is authenticated?', authStore.isAuthenticated);
I don't know if it is the main issue but u forgot to use a destructor on userStore doing
const isAuthenticated = authStore
It supposed to be
const { isAuthenticated } = toRefs(authStore);
toRefs to preserve reactivity after passing. It can be imported as
import { toRefs } from 'vue';

How can I use router inside pinia store [duplicate]

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
useRoute and useRouter must be used in Vue components 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
})
EDIT: 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: '/', component: 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.com/vuejs/pinia/discussions/1092
But I still don't know how to add this.route to Pinia.
Future reader, please comment if you know how to do it.
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(),
)();

Vue3 route.query empty

Trying to pass route query to axios request, but it is empty..
route.query returns empty in mounted. route.queryreturns {"filter[city]": "Vilnius" } in axios then
nextTick doesn't solve issue. Any tips?
import { ref, onMounted, nextTick } from 'vue';
import axios from 'axios';
import { useRouter, useRoute } from 'vue-router';
export default {
setup() {
const router = useRouter();
const route = useRoute();
onMounted(() => {
console.log(route.query); // log is {}
fetchApartments();
});
function fetchApartments() {
console.log(route.query); // log is {}
axios.get('/api/apartments').then(response => {
console.log(route.query); // log is { "filter[city]": "Vilnius" }
});
}
}
}
Route navigation is asynchronous. You need to wait for router.isReady for queries to be available
import {useRouter, useRoute} from 'vue-router';
export default {
setup() {
const router = useRouter();
const route = useRoute();
onMounted(async () => {
await router.isReady();
console.log(route.query);
});
}
}
Update your code like this:
...
import { computed } from 'vue'
...
and inside setup()
const route = useRoute();
const query = computed(() => route.query)
The missing part here is computed property.

Access app.config.globalProperties in vuex store

I got a vuex store like this:
const state = {
status: '',
};
const getters = {
//...
};
const actions = {
// ...
};
const mutations = {
// ...
};
export default {
namespaced: true,
state,
getters,
actions,
mutations,
}
Now I'd like to access app.config.globalProperties.$notify
In Vue.js2 I was using something like Vue.prototype.$notify, but this is not working anymore.
$notify is also provided like this:
app.use(routes)
.provide('notify', app.config.globalProperties.$notify)
.mount('#app')
Unfortunately I did not find any information about this in the docs yet.
So my question: How can I either inject $notify or access app.config.globalProperties within this store?
From your store and its modules, you could return a store factory -- a function that receives the application instance from createApp and returns a store:
// store/modules/my-module.js
const createStore = app => {
const mutations = {
test (state, { committedItem }) {
app.config.globalProperties.$notify('commited item: ' + committedItem)
}
}
return {
namespaced: true,
//...
mutations,
}
}
export default app => createStore(app)
// store/index.js
import { createStore } from 'vuex'
import myModule from './modules/my-module'
export default app =>
createStore({
modules: {
myModule: myModule(app)
}
})
Then use the store factory like this:
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import createStore from './store'
const app = createApp(App)
app.use(createStore(app)).mount('#app')
demo

React-native redux-saga error: takeLatest$1 requires a saga parameter

I created an App with some components and using the redux-saga in the following component:
// AlphaScreen.js
import ... // react & react-native
import { useSelector, useDispatch } from 'react-redux';
import { getUser } from '../redux/ducks/user';
const AlphScreen = props => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(getUser());
}, [dispatch]);
const users = useSelector((state) => state.user.user);
console.log(users);
return (
<View><Text>Blah</Text></View>
);
}
// redux/ducks/user.js
export const SET_USER = "SET_USER";
export const GET_USER = "GET_USER";
export const setUser = (user) => ({
type: SET_USER,
user // user: user
});
export const getUser = () => ({
tye: GET_USER
});
const initialState = {
user: undefined
};
const userReducer = (state = initialState, action) => {
switch(action.type) {
case SET_USER:
const {user} = action;
return {...state, user:user};
default:
return state;
}
};
export default userReducer;
// redux/defaultStore.js
import {applyMiddleware, combineReducers, createStore} from 'redux'
import createSagaMiddleware from "redux-saga";
import counterReducer from './ducks/counter'
import userReducer from './ducks/user';
import { watcherSaga } from './sagas/saga';
const reducer = combineReducers({
user: userReducer
});
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware];
const store = createStore(reducer, applyMiddleware(...middlewares));
sagaMiddleware.run(watcherSaga)
export default store;
// redux/sagas/saga.js
import { takeLatest } from 'redux-saga/effects';
import { handleGetUsers } from './handlers/user';
import { GET_USER } from '../ducks/user';
export function* watcherSaga() {
yield takeLatest(GET_USER, handleGetUsers); //<- getting error takeLatest$1 requires a saga parameter
}
// redux/sagas/handlers/user.js
import { call, put } from "redux-saga/effects";
import { setUser } from "../../ducks/user";
import { requestGetUser } from "../requests/user";
export function* handleGetUser(action) {
try {
const response = yield call(requestGetUser);
const { data } = response;
yield put(setUser(data));
} catch (error) {
console.log(error);
}
}
// redux/sagas/request/user.js
import axios from 'axios'
const requestGetUser = () => {
return axios.request({
method: "get",
url:"https://jsonplaceholder.typicode.com/users"
});
}
export { requestGetUser };
But I have following error:
takeLatest$1 requires a saga parameter
at node_modules/#redux-saga/core/dist/io-1d6eccda.js:37:10 in check
at node_modules/#redux-saga/core/dist/redux-saga-effects.dev.cjs.js:386:2 in validateTakeEffect
at node_modules/#redux-saga/core/dist/redux-saga-effects.dev.cjs.js:402:22 in takeLatest$1
at src/redux/sagas/saga.js:6:10 in watcherSaga
at node_modules/#redux-saga/core/dist/redux-saga-core.dev.cjs.js:1161:17 in next
at node_modules/#redux-saga/core/dist/redux-saga-core.dev.cjs.js:1112:6 in proc
at node_modules/#redux-saga/core/dist/redux-saga-core.dev.cjs.js:1371:19 in immediately$argument_0
at node_modules/#redux-saga/core/dist/redux-saga-core.dev.cjs.js:60:15 in immediately
at [native code]:null in runSaga
at src/redux/configureStore.js:16:0 in <global>
at App.js:7:0 in <global>
at node_modules/expo/AppEntry.js:3:0 in <global>
at http://192.168.1.154:19000/node_modules/expo/AppEntry.bundle?platform=android&dev=true&hot=false&minify=false:141908:3 in global code
The above error occurred in task watcherSaga
Any suggestions for fixing the error? Thanks
To me it looks like a typo, handleGetUsers vs handleGetUser :)