Why is my getter from store in App.vue class undefined - vue.js

I am building an electron app using vue, vuex and vuex-typescript. So I have the following code for my store using vuex-typescript:
export const dispaVuex = {
namespaced: true,
state: {
dispatch: new Dispatcher(appState),
},
getters: {
getFTime(state: DispatchState): boolean {
return state.dispatch.fistTime;
},
},
};
const {read} = getStoreAccessors<DispatchState, RootState>("Test");
export const readFtime = read(dispaVuex.getters.getFTime);
After adding the store to my vue instance I try and access the firstTime variable in my App.vue like this:
#Component
export default class App extends Vue {
// fTime: boolean;
get fTime(): boolean {
return readFtime(this.$store);
}
}
When looking at the debugger, everything in the store is initialized perfectly but my App instance has fTime as undefined.
Why would this be the case? Is there something I am not getting about the order of how things are made?
PS. firstTime is a member of the class Dispatcher

So after looking closely at the example given in the vuex-typescript readme, I noticed that I was passing the wrong namespace when calling the function getStoreAccessors.
Using dispaVuex instead of Test like this fixed the problem:
const {read} = getStoreAccessors<DispatchState, RootState>("dispaVuex");

Related

Issue using Socket.io with Pinia

I'm trying to use Pinia to manage some global state in a Vue.js app I'm building, specifically I want to share a Socket.io instance between various components and views. However I'm getting a
this.socketObject.emit is not a function
error when calling functions from the Socket.io instance, when I call them from a component other than the component/view the Socket.io instance was created in. Here's some excerpts of the code.
#/views/LobbyView.vue (This is where I create the Socket.io instance and pass it to the Pinia store, I can use emit fine in this file without any errors)
import io from "socket.io-client";
import { useSocket} from "#/store/index";
...
setup() {
const socketObject = useSocket();
return { socketObject};
},
...
async mounted() {
this.socketObject = await io("http://localhost:8000");
this.socketObject.emit("createNewRoom");
}
#/store/index.js Pinia store
import { defineStore } from "pinia";
...
export const useSocket = defineStore({
id: "socket",
state: () => {
return {socketObject: Object};
},
getters: {},
actions: {},
});
#/components/lobbySettings (this is the file where I have issues using Socket.io in via my Pinia store)
import { useSocket } from "#/store/index";
...
setup() {
const socketObject = useSocket();
return { socketObject};
},
...
methods: {
startGame() {
this.socketObject.emit("updateRoom", this.roomInfo);
},
},
When the start game method is called on a button press, if I catch the error I get
this.socketObject.emit is not a function
I don't quite understand why Pinia isn't giving me access to functions from my Socket.io instance, the store seems to be working fine for other data in my app, just cant call these functions.
useSocket returns a store, not socket instance. It should be used as:
const socketStore = useSocket();
...
socketStore.socketObject.emit(...)
io(...) doesn't return a promise, it's semantically incorrect to use it with await.
The use of Object constructor is incorrect. If a value is uninitialized, it can be null:
state: () => {
return {socketObject: null};
},
The mutation of store state outside the store is a bad practice. All state modifications should be performed by actions, this way they can be easily tracked through devtools, this is one of benefits of using a store.
At this point there's no benefit from packing socketObject inside a store. Socket instance could be either used separately from a store, or socket instance could be abstracted away and made reactive with store actions, etc.

Vue 3 - How to dispatch to Vuex store in setup

I have a project where I use Vue 3 and Vuex. This is my first time using Vue 3. I can't seem to get how to access Vuex inside the Setup method of a Vue 3 project.
I have a feature object. That is being set by a Childcomponent using the featureSelected method. Firstly in my setup I create a store constant with useStore; from import { useStore } from "vuex";. Then inside the featureSelected function I call the dispatch function on this store object store.dispatch("setPlot", { geometry: newFeature });.
I keep on getting an error telling me that the dispatch function does not exist on the store object: Uncaught TypeError: store.dispatch is not a function.
setup() {
const store = useStore;
const feature = ref();
const featureSelected = (newFeature) => {
feature.value = newFeature;
store.dispatch("setPlot", { geometry: newFeature });
};
return { feature, featureSelected };
},
useStore is a composable function which should be called using () like :
const store = useStore();

Understanding State and Getters in Nuxt.js: Getters won't working

i'm new to Vue and Nuxt and i'm building my first website in Universal mode with these framework.
I'm a bit confused on how the store works in nuxt, since following the official documentation i can't achieve what i have in mind.
In my store folder i have placed for now only one file called "products.js", in there i export the state like this:
export const state = () => ({
mistica: {
id: 1,
name: 'mistica'
}
})
(The object is simplified in order to provide a cleaner explanation)
In the same file i set up a simple getter, for example:
export const getters = () => ({
getName: (state) => {
return state.mistica.name
}
})
Now, according to the documentation, in the component i set up like this:
computed: {
getName () {
return this.$store.getters['products/getName']
}
}
or either (don't know what to use):
computed: {
getName () {
return this.$store.getters.products.getName
}
}
but when using "getName" in template is "undefined", in the latter case the app is broken and it says "Cannot read property 'getName' of undefined"
Note that in the template i can access directly the state value with "$store.state.products.mistica.name" with no problems, why so?
What am i doing wrong, or better, what didn't i understand?
Using factory function for a state is a nuxt.js feature. It is used in the SSR mode to create a new state for each client. But for getters it doesn't make sense, because these are pure functions of the state. getters should be a plain object:
export const getters = {
getName: (state) => {
return state.mistica.name
}
}
After this change getters should work.
Then you can use the this.$store.getters['products/getName'] in your components.
You can't use this.$store.getters.products.getName, as this is the incorrect syntax.
But to get simpler and more clean code, you can use the mapGetters helper from the vuex:
import { mapGetters } from "vuex";
...
computed: {
...mapGetters("products", [
"getName",
// Here you can import other getters from the products.js
])
}
Couple of things. In your "store" folder you might need an index.js for nuxt to set a root module. This is the only module you can use nuxtServerInit in also and that can be very handy.
In your products.js you are part of the way there. Your state should be exported as a function but actions, mutations and getters are just objects. So change your getters to this:
export const getters = {
getName: state => {
return state.mistica.name
}
}
Then your second computed should get the getter. I usually prefer to use "mapGetters" which you can implement in a page/component like this:
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters({
getName: 'products/getName'
})
}
</script>
Then you can use getName in your template with {{ getName }} or in your script with this.getName.

where to store global object in quasar framework

I'm re-writing my old app using Quasar Framework which is based on Vue, and I have a piece of code (class) which encapsulates websocket functionality.
It is a fairly simple concept: user travels from page to page in the app, but if he receives a message he can see a toast message/reply or a counter of unread messages increments.
I'm a little bit lost in the Quasar (Vue) architecture and here is my question:
Where would I store a global object which communicates with outside world, exists as long as the application exists and accessible from anywhere?
I read documentation of Quasar (Vue) but I still don't know where to put it. Vuex doesn't look right since it is not a state of the app. It is more like a faceless component.
Does it mean that I should use a plugin or Vue.prototype or a global mixin or something else?
I appreciate if someone can share their experience and a piece of code describing how to initialize and access this object's methods.
in my opinion:
Method 1. Use quasar plugin base on Vue prototype (sure you knew it):
plugins/foo.js
const fooModule = {
a: 1,
doTest() { console.log('doTest') }
};
export default ({Vue}) => {
Vue.prototype.$foo = fooModule;
}
quasar.conf.js
plugins: [
'i18n',
'axios',
'foo',
],
component bar.vue:
methods: {
test () { this.$foo.doTest() }
}
Method 2. Just use js module
Because js module is singleton. Wherever you import a js module, it all points to the same pointer.
So just have GlobalTest.js:
export default {
a: 1,
inc() { this.a = this.a + 1 }
}
And test1.js:
import GlobalTest from '/path/to/GlobalTest'
console.log(GlobalTest.a); // output 1
console.log(GlobalTest.inc()); // inc
And test2.js:
import GlobalTest from '/path/to/GlobalTest'
console.log(GlobalTest.a); // Assuming this was called after test1.js: output 2
I used quasar cli but I just consider quasar as a UI lib.
--- Updated ---
It is a fairly simple concept: user travels from page to page in the app, but if he receives a message he can see a toast message/reply or a counter of unread messages increments.
Depend on the requirements, If you need "reactive" you should use Vuex (best built-in reactive lib) + split the app state into modules,
but I only use Vuex when I need "reactive" and avoid it when I just need to read & write the value.
// ~/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
let store
export default function (/* { ssrContext } */) {
/* eslint-disable no-return-assign */
return store = new Vuex.Store({
modules: {...},
strict: process.env.DEV
})
}
export function ensureClientStoreExists () {
if (process.env.SERVER) {
return new Promise(() => { /* won't resolve */ })
}
if (process.env.CLIENT) {
if (store) {
return Promise.resolve(store)
}
return new Promise(resolve => {
setTimeout(resolve) // Avoid 'Maximum call stack size exceeded'
}).then(ensureClientStoreExists)
}
}
// Anywhere
import { ensureClientStoreExists } from '~/store/'
ensureClientStoreExists().then(store => {
console.log(store.state)
store.dispatch('XXX/YYY')
})

Vuex changes not impacting modules

I have a UserDialog component which leverages a part of the Vuex state-tree to determine whether it should display itself or not:
import { Component, Prop, Vue } from 'vue-property-decorator';
import { State, Getter, Mutation, Action, namespace } from 'vuex-class';
import { fk } from 'firemodel';
import { User } from '#/models/User';
const Users = namespace('users');
#Component({})
export default class UserDialog extends Vue {
#Prop() public id!: fk;
#Users.State public show: fk;
#Users.Getter public selectedUser: User;
#Users.Mutation public HIDE_USER_PROFILE: () => void;
public get showDialog() {
return this.show === undefined ? false : true;
}
}
From the parent component I am calling Vuex's commit('SHOW_USER_PROFILE', id) and thereby setting this ID it should update the UserDialog's show property accordingly.
I can see very clearly that the Vuex store has received the call to SHOW_USER_PROFILE and that indeed has updated the state in the state tree (this is through the Vue Developer plugin in the browser). But then when I switch over to the UserProfile component I see that it still has not received the state update.
Note: if I reload the page (aka, CMD-R) after having set the UserID I want to highlight, it reloads the components and because I'm using veux-persist, the ID is still set in the state tree. At this point the component DOES receive the correct state but when relying on the normal reactivity system it just doesn't work.
Can anyone help?
for additional context, here are a few more modules:
Store Definition::
export default new Vuex.Store<IRootState>({
modules: {
packages,
users,
searchCriteria,
snackbar
},
plugins: [FireModelPlugin, localStorage.plugin]
});
Users Mutations:
const mutations: MutationTree<IUsers> = {
selectUser(state, id: fk) {
state.selected = id;
},
SHOW_USER_PROFILE(state, id: fk) {
state.show = id;
},
HIDE_USER_PROFILE(state) {
state.show = undefined;
}
};
I have added a computed property to the UserDialog component above:
public get userId() {
return this.$store.state.users.show;
}
There was a thought that maybe this would be reactive whereas the #Users.State decorated show property was not. Unfortunately, they both perform exactly the same.
#Derek and I talked last night and realized that the cause of this problem was due to the state transitions to "undefined" which the current Reactive system does not handle (it should be fine when we get to Vue-NEXT with Object Proxies). The remaining code works just fine when I switch out the state transition from: undefined → string → undefined to null → string → undefined.
Many thanks to #Derek for spending the time.
In the example above you're directly calling the Vuex state store. When you do this from your component this is a one time get deale. The state store is not reactive and will never tell your computed property that it changed.
The correct way to get the reactivity you're looking for is to implement Vuex getters:
const store = new Vuex.Store({
state: {/*...*/},
getters: {
show(state) {
return state.show;
}
}
})
Then in your component:
computed: {
show() {
return this.$store.getters.show;
}
}
Read more about Vuex getters here: https://vuex.vuejs.org/guide/getters.html