Struggling to access state of vuex - vue.js

I'm going crazy over this as I can't understand what I might be missing.. so I'm basically using vuex to store the state of the user. I've setup a route and use Axios to make a call to this route to retrieve some info from the user. I can see in vue dev tools that the state and everything are set I'm just having a real hard time accessing it. If I do the following console.log(this.$store.state.baseSettings) in mounted then this shows the following:
Now I would have thought by adding .user onto that console.log I'd get exactly what I need but it shows an empty object.
I have also tried the following:
computed: {
user() {
return this.$store.state.baseSettings.user;
},
},
This works if I do {{ user }} in the template itself but if I try and access this.user in computed, mounted, or any methods I also get an empty object. Is there any reason for this? Am I missing something simple/obvious here?
Any help would be greatly appreciated! Here's my full code:
app.js:
import Vue from 'vue';
import Vuetify from 'vuetify';
import router from '../../router';
import store from './store';
import {userComputed, settingsMethods} from './store/helpers';
import App from '../App.vue';
new Vue({
router,
store,
vuetify: new Vuetify(),
render: h => h(App),
computed: {
...userComputed,
},
methods: {
...settingsMethods,
},
mounted() {
return this.getAllSettings();
},
}).$mount('#app');
App.vue
<template>
<v-app>
{{ user }}
<v-main :class="!user ? 'background-img' : null">
<v-container fluid>
<nav-bar/>
<router-view/>
</v-container>
</v-main>
</v-app>
</template>
<script>
export default {
computed: {
user() {
// Works but only in above <template></template>
return this.$store.state.baseSettings.user;
},
},
mounted() {
// Returns object with user data (see screenshot).
console.log(this.$store.state.baseSettings);
// Empty object!
console.log(this.$store.state.baseSettings.user);
// Empty object
console.log(this.user)
}
}
</script>
baseSettings.js:
const state = {
user: {},
};
const getters = {
getUser: state => _.keys(state.user),
};
const actions = {
getAllSettings({ commit }) {
return axios.get('settings').then(baseSettings => {
commit('setUser', _.get(baseSettings, 'data.user', {}));
});
},
};
const mutations = {
setUser(state, user) {
state.user = user;
},
};
export default {
namespaced: true,
state,
getters,
actions,
mutations,
};
helpers.js:
import { mapActions, mapState } from 'vuex';
export const userComputed = {
...mapState('baseSettings', ['user']),
};
export const settingsMethods = {
...mapActions('baseSettings', ['getAllSettings']),
};
index.js
import Vue from 'vue';
import Vuex from 'vuex';
import baseSettings from '../modules/baseSettings';
Vue.use(Vuex);
const debug = process.env.NODE_ENV !== 'production';
export default new Vuex.Store({
modules: {
baseSettings,
},
strict: debug,
});
Thanks so much!

This seems to be an unfulfilled promise problem.
You do both, retrieving the getAllSettings() as well as trying to access the $store independently of each other at the same lifecycle step. Therefore it is not guaranteed that the Axios call has already reported data and saved it to the store when you try to access it from there (resulting in the empty object at runtime).
However since the computed property reruns once the dependant variable changes, it will display correct in your component, as this happens after the mounted() lifecycle step, when the Axios call has run through.

Related

vuex unknown action type: displayArticles

Why it doesn't display the json coming from jsonplaceholder?
Did I miss something here? This is just my first time using Vuex.
By the way, I separated the files so that I can debug it easily and I thought it's a good practice for me because I'm planning to implement vuex in a bigger project.
Here is my index.js:
import Vue from 'vue';
import Vuex from 'vuex';
import articles from './modules/articles';
//Load Vuex
Vue.use(Vuex);
//Create store
export default new Vuex.Store({
modules: {
articles
}
})
Here is my articles.js .
import axios from 'axios';
//state
const state = {
articles: []
};
//actions
const actions = {
loadArticles({ commit }) {
axios.get('https://jsonplaceholder.typicode.com/todos')
.then(response => response.data)
.then(articles => {
commit('displayArticles', articles,
console.log(articles))
})
}
};
//mutations
const mutations = {
displayArticles(state, articles) {
state.articles = articles;
}
};
//export
export default {
state,
getters,
actions,
mutations
};
and lastly my home.vue that will display the data from the vuex:
<template>
<section>
<h1>HI</h1>
<h1 v-for="article in articles" :key="article.id">{{article.id}}</h1>
</section>
</template>
<script>
import { mapState } from "vuex";
export default {
mounted() {
this.$store.dispatch("displayArticles");
},
computed: mapState(["articles"])
};
</script>
you have to dispatch the action, so in your .vue file you have to write :
mounted() {
this.$store.dispatch("loadArticles");
},
and to get the list of articles in your component you should use getter in your Store:
const getters = {
getArticles: state => {
return state.articles;
}
and the computed will be like this:
computed:{
getArticlesFromStore(){
return this.$store.getters.getArticles;
}
}
and then you call the computed leement in your HTML:
<h1 v-for="article in getArticlesFromStore" :key="article.id">{{article.id}}</h1>
You are trying to dispatch mutation. You need to use commit with mutations or move your displayArticles to actions. I imagine you meant to dispatch loadArticles?

Nuxt.js unknown store

I'm new to VueJS and try to use the store with NuxtJS.
So, i want to get this (https://nuxtjs.org/guide/vuex-store/) to work.
My store ./store/index.js
import { mapActions, mapGetters } from 'vuex'
export const state = () => ({
temperature: '1234'
})
export const mutations = {setName
setTemperature(state, payload) {
state.temperature = payload
}
}
export const actions = {
getWeatherData({ commit }) {
console.log("set weather to 123")
commit('setTemperature', '123')
}
}
export const getters = {
storeTemperature(state) {
return state.temperature
}
}
export const mixin = {
computed: {
...mapGetters([{
myTemperature: 'weather/storeTemperature'
}])
},
methods: {
...mapActions({
loadWeatherData: 'weather/getWeatherData'
})
}
}
export default mixin
Now i have a simple component to display the temperature:
<template>
<div class="label">
{{ testB }}
</div>
</template>
<style lang="scss" src="./Label.scss"></style>
<script>
import { mixin as WeatherMixin } from '../../store'
export default {
name: 'Label',
//mixins: [WeatherMixin],
props: {
content: {
Type: String,
default: ''
}
},
computed: {
testB () {
return this.$store.state.store.temperature
}
}
}
</script>
I tried to use mapGetters and use it like:
testB () {
return this.myTemperature
}
but this didn't work.
So i tried to use mapState via:
// store
computed: {
...mapState({ temperature: 'temperature' })
}
and use it in the component like
<div class="label">
{{ temperature }}
</div>
But i always didn't get the default value of 1234.
The Vue Console didn't find the store:
But forward to the NuxtJS documentation:
Nuxt.js will look for the store directory, if it exists, it will:
Import Vuex,
Add the store option to the root Vue instance.
What i need to get the store working as expected and how i can access the store state properties? Did i miss something?
To properly manage Vuex in NuxtJS, use namespaced Vuex modules, to keep your state logic properly organized.
For a globally accessible state, you can add these to the ./store/index.js file, while for namespaced modules create a folder with a name of your namespaced module (Preferably no spaces.), then in this folder create files with names actions.js for actions, state.js for states, getters.js for getters and mutations.js for your mutations and export default from each.
You can then access the namespaced state with this.$store.state.<NAMESPACED MODULE NAME>.<STATE VARIABLE>

Vuex. Again: Computed properties are not updated after Store changes. The simplest example

I did really read all Google!
Store property "sProp" HAS initial value (=10);
I use Action from component to modify property;
Action COMMITS mutation inside the Store Module;
Mutation uses "Vue.set(..." as advised in many tickets
Component uses mapState, mapGetters, mapActions (!!!)
I also involve Vuex as advised.
I see initial state is 10. But it is not changed after I press button.
However if I console.log Store, I see it is changed. It also is changed in memory only once after the 1st press, but template always shows 10 for both variants. All the other presses do not change value.
Issue: my Computed Property IS NOT REACTIVE!
Consider the simplest example.File names are in comments.
// File: egStoreModule.js
import * as Vue from "vue/dist/vue.js";
const state = {
sProp: 10,
};
const getters = {
gProp: (state) => {
return state.sProp;
}
};
const mutations = {
//generic(state, payload) {},
mProp(state, v) {
Vue.set(state, "sProp", v);
},
};
const actions = {
aProp({commit}, v) {
commit('mProp', v);
return v;
},
};
export default {
namespaced: true
, state
, getters
, mutations
, actions
}
And it's comsumer:
// File: EgStoreModuleComponent.vue
<template>
<!--<div :flag="flag">-->
<div>
<div><h1>{{sProp}} : mapped from Store</h1></div>
<div><h1>{{$store.state.egStoreModule.sProp}} : direct from Store</h1></div>
<button #click="methChangeProp">Change Prop</button>
<button #click="log">log</button>
</div>
</template>
<script>
import {mapActions, mapGetters, mapState} from "vuex";
export default {
name: 'EgStoreModuleComponent',
data() {
return {
flag: true,
};
},
computed: {
...mapState('egStoreModule', ['sProp']),
...mapGetters('egStoreModule', ['gProp'])
},
methods: {
...mapActions('egStoreModule', ['aProp']),
methChangeProp() {
const value = this.sProp + 3;
this.aProp(value);
//this.flag = !this.flag;
},
log() {
console.log("log ++++++++++");
console.log(this.sProp);
},
}
}
</script>
<style scoped>
</style>
Here is how I join the Store Module to Application:
// File: /src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import collection from "./modules/collection";
import egStoreModule from "./modules/egStoreModule";
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {},
getters : {},
mutations: {},
actions : {},
modules : {
collection,
egStoreModule
}
});
Finally: main.js
import Vuex from 'vuex'
import {store} from './store'
//...
new Vue({
el: '#app',
router: router,
store,
render: h => h(App),
})
It all works if only I use "flag" field as shown in comments.
It also works if I jump from page and back with Vue-router.
async/await does not help.
I deleted all under node_modules and run npm install.
All Vue/Vuex versions are the latest.

vuex unknown action type when attempting to dispatch action from vuejs component

I'm using laravel, vue and vuex in another project with almost identical code and it's working great. I'm trying to adapt what I've done there to this project, using that code as boilerplate but I keep getting the error:
[vuex] unknown action type: panels/GET_PANEL
I have an index.js in the store directory which then imports namespaced store modules, to keep things tidy:
import Vue from "vue";
import Vuex from "vuex";
var axios = require("axios");
import users from "./users";
import subscriptions from "./subscriptions";
import blocks from "./blocks";
import panels from "./panels";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
},
actions: {
},
mutations: {
},
modules: {
users,
subscriptions,
blocks,
panels
}
})
panels.js:
const state = {
panel: []
}
const getters = {
}
const actions = {
GET_PANEL : async ({ state, commit }, panel_id) => {
let { data } = await axios.get('/api/panel/'+panel_id)
commit('SET_PANEL', data)
}
}
const mutations = {
SET_PANEL (state, panel) {
state.panel = panel
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
Below is the script section from my vue component:
<script>
import { mapState, mapActions } from "vuex";
export default {
data () {
return {
}
},
mounted() {
this.$store.dispatch('panels/GET_PANEL', 6)
},
computed:
mapState({
panel: state => state.panels.panel
}),
methods: {
...mapActions([
"panels/GET_PANEL"
])
}
}
</script>
And here is the relevant code from my app.js:
import Vue from 'vue';
import Vuex from 'vuex'
import store from './store';
Vue.use(Vuex)
const app = new Vue({
store: store,
}).$mount('#bsrwrap')
UPDATE:: I've tried to just log the initial state from vuex and I get: Error in mounted hook: "ReferenceError: panel is not defined. I tried creating another, very basic components using another module store, no luck there either. I checked my vuex version, 3.1.0, the latest. Seems to be something in the app.js or store, since the problem persists across multiple modules.
Once you have namespaced module use the following mapping:
...mapActions("panels", ["GET_PANEL"])
Where first argument is module's namespace and second is array of actions to map.

How to watch two more stores in Vue.js and Vuex?

I'm using Vue.js and Vuex.
I want to watch the value of store (I'll share my sample codes in the last of this post).
But I have encountered the error "Error: [vuex] store.watch only accepts a function."
In this Web site, I found how to use "single" store.
https://codepen.io/CodinCat/pen/PpNvYr
However, in my case, I want to use "two" stores.
Do you have any idea to solve this problem.
●index.js
'use strict';
import Vue from 'vue';
import Vuex from 'vuex';
import {test1Store} from './modules/test1.js';
import {test2Store} from './modules/test2.js';
Vue.use(Vuex);
export const store = new Vuex.Store({
modules: {
test1: test1Store,
test2: tes21Store,
}
});
●test1.js
'use strict';
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const checkerStore = {
namespaced: true,
state: {
count: 1
},
getters: {
getCount(state){
return state.count;
}
}
};
export default {test1};
●test.vue
<template>
{{ $store.state.couunt }}
</template>
<script>
import {store} from './store/index.js';
export default {
data: function () {
return {
}
},
store: store,
methods: {
},
mounted() {
setInterval(() => { this.$store.state.count++ }, 1000);
this.$store.watch(this.$store.getters['test1/getCount'], n => {
console.log('watched: ', n)
})
}
}
}
</script>
You're getting that error because in your example code, the first argument you've passed to watch is, as the error suggests, not a function.
The argument is actually implemented using the native JavaScript call Object.defineProperty underneath the hood by Vue, so the result of doing store.getters['test1/getCount'] as you have, is actually a number.
I don't think it's recommended to use watch in a scenario like yours, but if you insist, you can access count by providing an alternate argument form, something like:
this.$store.watch((state, getters) => getters['test1/getCount'], n => { ... })