How do I access the Vuex store in Vue when using the setup() method?
I don't have access to the regular this.$store variable anymore.
According to the composition API documentation you have to use the following syntax:
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
return {
// access a state in computed function
count: computed(() => store.state.count),
// access a getter in computed function
double: computed(() => store.getters.double)
}
}
}
Related
When in preview mode (#see https://nuxtjs.org/docs/features/live-preview) Vuex getters do not appear to work, at least with data that's fetched.
Accessing the data from the store directly works.
Accessing the data via a getter which then calls the same code from the store, returns undefined.
Data is fetched (technically, using axios) via nuxtServerInit, but the issue is the same if I change that to fetch (using axios) via asyncData from within my template.
These are two simple examples (using actual code that exhibits this issue)
This only happens when preview mode is enabled and using target: static
enablePreview()
Why? Why don't getters work? It's like the state that the getters are accessing is different to the state that I access when I access it directly.
Example 1:
/store/index.js
export const getters = {
getURLData(state) {
return state?.urls ?? [];
},
getPageData: (state, getters) => (url) => {
const a = state?.urls; // [array,of,urls]
const b = getters.getURLData; // undefined
...
}
Example 2:
store/index.js
export const getters = {
getSettings(state) {
return state?.settings;
}
}
whatever.vue
export default {
head() {
const a = this.$store.getters.getSettings; // undefined
const b = this.$store.state?.settings; // {data}
}
}
I think that the problem is in your way of calling vuex getters, try to use MapGetters instead of accessing it directly via this.$store.
import { mapGetters } from 'vuex'
export default {
.....
computed: {
...mapGetters({
settings: 'getSettings',
}),
}
....
}
In Vue 2 I used to import Vue and access global properties like this (from the store):
import Vue from 'vue'
Vue.config.myGlobalProperty
According to the new documentation, in Vue 3 the global properties are declared using the app object returned by createApp:
const app = createApp({})
app.config.globalProperties.myGlobalProperty
And then accessed in the child component by simply calling this.myglobalProperty
But how to access that global property from the store? I tried exporting/importing the app object but it doesn't work (probably due to the app being created after its import in the store).
With Vue 2 I used to use global properties in the store like this:
Declaration in the main.js file:
import Vue from 'vue'
Vue.config.myglobalProperty = 'value'
Usage in the store:
import Vue from 'vue'
Vue.config.myglobalProperty
Is there a good way to do that in Vue3?
I noticed a better way to provide/inject properties but it works with child component only and not with the store.
You could pass the app instance to a store factory:
// store.js
import { createStore as createVuexStore } from 'vuex'
export const createStore = (app) => {
return createVuexStore({
actions: {
doSomething() {
if (app.config.globalProperties.myGlobal) {
//...
}
}
}
})
}
And use it in main.js like this:
// main.js
import { createApp } from 'vue'
import { createStore } from './store'
const app = createApp({})
const store = createStore(app)
app.use(store)
app.mount('#app')
If your store modules need access to the app instance, you could use the same technique above with a module factory:
// moduleA.js
export default (app) => {
return {
namespaced: true,
actions: {
doSomething() {
if (app.config.globalProperties.myOtherGlobal) {
//...
}
}
}
}
}
And import it into your store like this:
// store.js
import moduleA from './moduleA'
import { createStore as createVuexStore } from 'vuex'
export const createStore = (app) => {
return createVuexStore({
modules: {
moduleA: moduleA(app),
}
})
}
If you do not use Vuex, etc., you can easily create your store via provide/inject on the application itself, as in the example (the example is simplified for understanding):
const createState = () => reactive({counter: 0, anyVariable: 1});
const state = createState();
const key = 'myState';
// example of reactivity outside a component
setInterval(() => {
state.counter++;
}, 1000);
const app = createApp({});
app.provide('myState', state); // provide is used on the whole application
As you can see, your own store can be completely used outside the component.
Inside components, you can use (example):
setup() {
...
const globalState = inject('myStore'); // reactive => {counter: 0, anyVariable: 1}
...
return {globalState, ...}
}
Accordingly, you can have multiple stores in multiple Vue applications.
I hope this example will help you somehow.
I have this gtag (analytics plugin) that I can access on my components but never on my store.
I would appreciate any opinions. Thanks
plugins/vue-gtag.js
import Vue from "vue"
import VueGtag from "vue-gtag"
export default ({ app }, inject) => {
Vue.use(VueGtag, {
config: {
id: process.env.ga_stream_id
}
})
}
store/gaUserProperty.js
import Vue from "vue"
import { User } from "~/models/user/User"
export const states = () => ({})
const getterObjects = {}
const mutationObjects = {}
Object.keys(states).forEach(key => {
getterObjects[key] = state => state[key]
mutationObjects[key] = (state, value) => (state[key] = value)
})
export const state = () => states
export const getters = { ...getterObjects }
export const mutations = { ...mutationObjects }
export const actions = {
async sendUserProperties({ dispatch, commit }) {
let res = await this.$UserApi.getUser()
if (!(res instanceof User)) {
} else {
// I can access this on my components and pages but for some reason not here....
console.log(this.$gtag)
}
}
}
To import this properly, I would export the instance (or any of its internals) from main.(ts|js):
const Instance = new Vue({...whatever});
// only export what you need in other parts of the app
export const { $gtag, $store, $t, $http } = Instance;
// or export the entire Instance
export default Instance;
now you can import it in your store:
import Instance from '#/main';
// or:
import { $gtag } from '#/main';
// use Instance.$gtag or $gtag, depending on what you imported.
As other answers mentioned, in current Vuex version the Vue instance is available under this._vm inside the store. I'd refrain from relying on it, though, as it's not part of the exposed Vuex API and not documented anywhere. In other words, Vuex developers do not guarantee it will still be there in future versions.
To be even more specific, Vue's promise is that all v2 code will work in v3. But only if you use the exposed API's.
And the discussion here is not even on whether it will be removed or not (it most likely won't). To me, it's more a matter of principle: if it starts with a _ and it's not documented, it translates into a message from authors: "We're reserving the right to change this at any time, without warning!"
You can access the Vue instance through this._vm in the Vuex store, so you would just need to do:
console.log(this._vm.$gtag)
That should do the trick.
According to nuxtjs the plugins are available in the store actions.
https://nuxtjs.org/docs/directory-structure/plugins
Sometimes you want to make functions or values available across your
app. You can inject those variables into Vue instances (client side),
the context (server side) and even in the Vuex store. It is a
convention to prefix those functions with a $.
I have two Vuex stores inside the Nuxt store directory. I'm not able to access one store's states from another, even through routeState.
Store 1: index.js
export const state = () => ({
token: false
})
export const getters = {}
export const mutations = {}
export const actions = {}
Store 2: api.js
export const state = () => ({
apiBase: 'https://myurl.com/'
})
export const getters = {
getAPI: (state, rootState) => {
// Need the state token from index.js here
},
}
export const mutations = {}
export const actions = {}
Here,
state returns the state variables in api.js, that is apiBase
routeState returns the getters in api.js
this is undefined inside Vuex getters, it doesn't work
How do I access the state or getters from index.js?
The rootState is exposed ad the third argument into a getter.
const getters = {
getApi: (state, getters, rootState) => {
// Access the state token from index.js
rootState.token
}}
Check this page for more details Vuex Docs
I am running a simple Axios call like so:
.get('https://myAPI.com/')
.then(response => {
this.info = response.data
})
And then display the data through a v-for array loop on my components. The problem is that I am running this mounted Axios call on each component I use it for. For example, I have a component for desktop screens that uses this axios call to display data in sidebar, while my mobile screen component uses the exact same axios call too display in a header.
The problem is that I am running multiple calls to the same API since each component is using the mounted axiox function.
Is there a way to run this call once and then utilize the v-for loop on each component?
Use Vuex for such task.
I'll make a very simple example.
Install vuex and axios in your project
later create a file in your project call, store.js.
store.js
import Vue from "vue";
import Vuex from "vuex";
import axios from "axios";
const store = new Vuex.Store({
state: {
info : []
},
mutations: {
updateInfo (state, info) {
state.info = info
}
},
actions: {
fetchData({commit}) {
axios.get('https://myAPI.com/')
.then(response => {
commit('updateInfo', response.data )
})
}
}
})
in your main.js import store.js file
import store from "./store";
new Vue({
...
store,
...
});
in your App.vue dispatch 'updateInfo' action.
App.vue
...
created() {
this.$store.dispatch("fetchData");
}
...
And in the component you want to use the info data component, set:
...
computed: {
info() {
return this.$store.state.info
}
},
...
and use info to render the elements with the v-for directive.
This info refers the array of elements you bring
OK, I've found a way to handle this without Vuex. My example: I have two components TrainingCourseComponent and CertificateComponent.
In TrainingCourseComponent:
data() {
return {
trainings : {},
},
methods:{
loadTrainingCenters(){
axios.get("/trainingCourse")
.then(({data}) => {
this.trainings = data;
Event.$emit('globalVariables', data);
});
}
}
created(){
this.loadTrainingCenters();
}
and you can do this in any other component but in this case CertificateComponent(you can define it in mounted() or created() method it doesn't matter:
data() {
return {
training_courses:{}
}
}
mounted(){
Event.$on('globalVariables', (trainings) => {
this.training_courses = trainings;
});
}
p.s. I guess you know but just in case Event is a global Vue instance defined in app.js that I use for different kind of stuff :)
app.js
/**
* some custom event
*/
window.Event = new Vue();