Problems with vuex states - vue.js

When my application is started I make two requests to get some information (company and user informations) that I will use basically in all my components. I store this information in store.js
My App.vue
<template>
<router-view></router-view>
</template>
export default{
methods: {
getCompanyIndormation(){
//Vuex function to store company information
this.setCompanyInformation(someinformation)
},
getUserInformation(){
//Vuex function to store user information
this.setUserInformation(someinformation)
}
}
}
My store.js
export default new Vuex.Store({
state: {
user: {
id: '',
name: '',
},
company: {
name: '',
subdomain: ''
}
},
mutations: {
setUserInformation: (state, obj) => {
state.user.id = obj.id;
state.user.name = obj.name;
},
setCompanyInformation: (state, obj) => {
state.company.name = obj.name;
state.company.subdomain = obj.subdomain;
}
}
})
So far everything works perfectly. My problem was when I tried to retrieve some information from the company in a mixin I own.
My mixin.js
import { mapState } from 'vuex';
const myMixin = {
computed: {
...mapState(['company'])
},
methods: {
$getCompanyUrl(){
return 'https:// '+this.company.subdomain+'/contact'
}
}
}
My problem here is that in some cases the function of my mixin normally returns the entire url, for example: https://domain1.com/contact but sometimes it returns https:///contact, that is, he didn't find it still the domain. Does anyone have any suggestions on how I can solve this problem? Do I add a watcher to see when the company's information has changed in the store or do I expect to finish all initial requirements before even rendering the router-view?

Maybe just create a getter for company url in your store.
getters: {
companyUrl: state => `https://${state.company.subdomain}/contact`,
},
And then use mapGetters anywhere else. The getters are meant to be watching the state changes.

Related

Why action of Vuex returns a promise<pending>?

I have an action in Vuex actions which commit a mutation that it take a payload from the component, that is a number of the index for returning an object, it works fine on Vuex js file meaning that shows the selected item on the console, as I said it gets index from the payload,
but on the component, it gives me Promise <Pending>, why that's happening? for now, I do not use any API for my Nuxt/Vue app, but I will, and for now, I just want to know why this is happening and what is the best solution for solving this
Here my Vuex codes:
export const state = () => ({
articles: [
{
uid: 0,
img: 'https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/1.jpg',
link: '/articles/1',
},
{
uid: 1,
img: 'https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/2.jpg',
link: '/articles/2',
},
],
})
export const getters = {
getArticles(state) {
return state.articles
},
}
export const mutations = {
getSpeceficArticle(state, payload) {
return state.articles[payload]
},
}
export const actions = {
getSpeceficArticle({ commit }, payload) {
commit('getSpeceficArticle', payload)
},
}
and here my component codes:
<template>
<div class="article">
{{ getSpeceficArticle() }}
<div class="article__banner">
<img src="" alt="" />
</div>
<div class="article__text">
<p></p>
</div>
</div>
</template>
<script>
export default {
name: 'HomeArticlesArticle',
data() {
return {
item: '',
}
},
// computed: {},
methods: {
async getSpeceficArticle() {
return await this.$store.dispatch('articles/getSpeceficArticle', 0)
},
},
}
</script>
actions are used to update the state they are like mutations but the main difference between them is that actions can include some asynchronous tasks, if you want to get a specific article at given index you should use a getter named getArticleByIndex :
export const getters = {
getArticles(state) {
return state.articles
},
getArticleByIndex:: (state) => (index) => {
return state.articles[index]
}
}
then define a computed property called articleByIndex :
<script>
export default {
name: 'HomeArticlesArticle',
data() {
return {
item: '',
}
},
computed: {
articleByIndex(){
return this.$store.getters.articles.getArticleByIndex(0)
}
},
methods: {
},
}
</script>
#Mohammad if you find yourself using a lot of getters/actions etc from Vuex and they're starting to get a little wordy, you can bring in mapGetters from Vuex and rename your calls to something a little more convenient. So your script would become,
<script>
import { mapGetters } from 'vuex'
export default {
name: 'HomeArticlesArticle',
data() {
return {
item: '',
}
},
computed: {
articleByIndex(){
return this.getArticleByIndex(0)
}
},
methods: {
...mapGetters({
getArticleByIndex: 'articles/getArticleByIndex',
})
},
}
</script>
You can add ...mapGetters, ...mapActions to your computed section also.
since there is no web service call in vuex action, try to remove async and await keywords from the component.
Later when you add a webservice call than you can wrap action body in new Promise with resolve and reject and then you can use async and await in component. let me know if this works for you.

Vuex state changes are not propagated to Vue component template

I just started working on Vue and Vuex. I have created a component with its state data in Vuex. After an action, I can see my state changes applied in mutation, however, my Vue component is still not able to pick the new changes up.
Here's my store file:
const state = {
roomInfo: {
gameID: null,
userID: null,
},
seats: null,
};
const getters = {
seats: state => state.seats,
roomInfo: state => state.roomInfo,
};
const actions = {
async streamSeats({ commit }) {
let connection = new WebSocket(`ws://localhost:8080/api/game/${state.roomInfo.gameID}/seats/${state.roomInfo.userID}`)
connection.onmessage = function(event) {
commit('setSeats', event.data);
}
connection.onopen = function() {
console.log("Successfully connected to the echo websocket server...")
}
connection.onerror = function(event) {
console.log("ERRR", event)
}
},
async setRoomInfo({ commit }, roomInfo) {
commit('setRoomInfo', roomInfo);
},
};
const mutations = {
setSeats: (state, seats) => {
state.seats = seats
// I can see changes here properly
console.log(seats);
},
setRoomInfo: (state, roomInfo) => {
state.roomInfo.gameID = roomInfo.gameID;
state.roomInfo.userID = roomInfo.userID;
if (roomInfo.seatNumber === 1) {
state.seats.p1.id = roomInfo.userID;
}
},
};
export default {
state,
getters,
actions,
mutations,
};
And this is my component:
<template>
{{ seats }}
</template>
<script>
/* import API from '../api' */
import { mapGetters, mapActions } from 'vuex';
export default {
name: "Seats",
methods: {
...mapActions([
'streamSeats',
'setRoomInfo',
]),
},
computed: {
...mapGetters([
'seats',
'roomInfo',
'setSeats',
]),
},
watch: {
roomInfo: {
handler(newValue) {
if (newValue.userID && newValue.gameID) {
this.streamSeats();
}
},
deep: true,
},
},
components: {},
data: function() {
return {
alignment: 'center',
justify: 'center',
}
},
created() {
let gameID = this.$route.params.id
this.setRoomInfo({
gameID: gameID,
userID: this.$route.params.userID,
seatNumber: 1,
});
},
}
</script>
As you can see, I'd like to change the state data for seats inside state, after it connects to websocket server.
I have spent a long time trying to figure this out with no luck. I've tried to use mapstate, data, and a few other tricks without any luck. I tried all the suggested solutions in similar stackoverflow threads as well. I'd really appreciate if someone could give me some hints on how to pass this obstacle.
There are some mismatch when you define getters and call mapGetters
store
const getters = {
seatsd: state => state.seats, // there is a typo in seats, you declared seatsd
roomInfo: state => state.roomInfo,
};
component
computed: {
...mapGetters([
'seats',
'roomInfo',
'setSeats', // this is not getters, this is mutations
]),
},
Thank you for looking at it. I installed Vuejs chrome extension today. Apparently it changed the way errors were displayed in chrome dev console. I just noticed I had a few uncaught errors elsewhere, which didn't allow the code to go through these parts properly. After resolving those issues, I was able to see the data in my component.

Using store data in Component pulls in default values, instead of updated ones

I am new to Vue, and am trying to use data from a store in a component. This data gets set when the app loads, and I have confirmed that it is getting set properly.
However, when I try to access this data from the store in a component (I'm using mapState), all I get are the default values for the store, and not the data that was set.
The store I am trying to access is:
//user.js
const defaults = {
emailAddress: '',
gender: 'N/A',
registered: false,
userName: '',
};
const state = {
user: { ...defaults, },
};
const getters = {};
const actions = {
login ({ commit, }) {
api.post(/loginurl).then((response) => {
commit('setUserInfo', { ...defaults, ...response, });
});
},
};
const mutations = {
setUserInfo (state, user) {
state.user = {...state, ...user,};
},
};
const user = {
namespaced: true,
state,
getters,
actions,
mutations,
};
export default user;
I've confirmed that all data is getting set correctly in setUserInfo.
However, when I access this in a component like so:
<template>
<div class="user-info">
<p> {{ user }} </p>
</div>
</template>
<script>
import { mapState } from 'vuex';
export default {
computed: {
...mapState('user', { user: (state) => state.user, },),
}
};
</script>
All the defaults for user print out in my template, instead of the values that were set in 'setUserInfo.
I will also add, that if I just try to access this via a computed property directly, I also get the defaults. For example:
userInfo () {
return this.$store.state.user;
},
Will also just return the defaults.
Do I need to set up a getter in the store, and then just use that in my component instead? Any help would be much appreciated!!!

Fetching data with axios from a fake API into a Vuex store doesn't produce the desired outcome

This is my store.js code which contains an array named posts which is getting the data from the api
export const store=new vuex.Store({
state:{
posts:[],
},
mutations:{
getallpost(state,posts) {
state.posts = posts
}
},
actions: {
getallpost: ({commit}) =>{
var self =this;
axios.get('https://jsonplaceholder.typicode.com/posts')
.then(function (response) {
commit(self.posts=response.data)
commit(console.log('Data: ',response.data));
})
.catch(function (error) {
console.log(error);
});
},
}
});
This is my mainpage.js code: here i am mapping the actions and the state still i am not getting the data displayed on my window
import {mapActions} from 'vuex'
import {mapState} from 'vuex'
import {store} from "../../store/store";
import axios from "axios";
export default {
name: 'Mainpage',
components:{Counter,Result,store},
data(){
return{
posts:[],
}
},
methods:{
...mapActions([
'getallpost']),
...mapState([
'getallpost'
])
}
};
They way you are committing is incorrect. The first argument is a string of the mutation name, the second one is the value.
Your commit in your store should look like that in your action:
commit('setPosts', response.data)
Note that I've renamed your mutation from getallpost to setPosts as this makes it semantically more accurate and avoids naming collisions, so basically:
mutations:{
setPosts(state, posts) {
state.posts = posts
}
},
That should do the job of storing the data. Also, get rid of the var self = this. When using store you never want to use this, the current state of the store will always be given to your mutation through the parameter in the function, to ensure that everything runs synchronous without side-effects.
So far so good. Now in order to be able to receive the fetched posts from your store, you need a getter in your store module:
getters: {
getPosts: state => state.posts,
},
Finally add the getter to your component's methods:
...mapGetters({
storePosts: 'getPosts'
})
You can now use the posts in your template by simply using {{ storePosts }}

VueJS - Vuex Assign data in a mutation

I'm getting used to Vuex as I need to be able to have a store that I can easily access and update when another component is changed.
Currently, my store looks like the following:
import vue from 'vue';
import Vuex from 'vuex';
vue.use(Vuex);
export default new Vuex.Store({
state: {
users: {
columns: [],
model: [],
}
},
mutations: {
fetchUsers: function(state) {
axios.get(`/users?search_input=`)
.then(function(response) {
});
}
}
});
The columns and model is dynamically pulled down from an ajax request and my Users looks like the following:
<script>
export default {
mounted() {
this.$store.commit('fetchUsers');
},
computed: {
columns() {
return this.$store.state.users.columns;
}
model() {
return this.$store.state.users.model
}
},
}
</script>
My problem is that the application needs to have data preloaded from Ajax. For example columns are set from an ajax request in fetchUsers and inside the Users I use this.$store.commit('fetchUsers'); but is there an alternative way I can do that without using commit preferably inside the store itself?
In the store u can load ur data asynchronously via actions then commit the changes using a mutation.
import vue from 'vue';
import Vuex from 'vuex';
vue.use(Vuex);
export default new Vuex.Store({
state: {
users: {
columns: [],
model: [],
}
},
actions: {
fetchUsers: function( context ) {
axios.get(`/users?search_input=`)
.then( function( response ) {
context.commit( "FETCHUSERS", {
columns: response.columns,
model: response.model
});
});
}
}
mutations: {
FETCHUSERS: function( state, payload ) {
state.users.columns = payload.columns;
state.users.model = payload.model;
}
}
});
Dispatch the action from the component
<script>
export default {
.....
methods: {
fetchUsers: function() {
this.$store.dispatch( "fetchUsers" );
}
}
}
</script>
Mutations must be synchronous: https://vuex.vuejs.org/en/mutations.html
You should move ajax request into actions, which can be async. You get data in the action, commit mutation giving received data as a payload, and mutation assigns a value to the state property.
Alternatively, you can make async request in component method, and assign a value to the store property directly: this.$store.state.prop = value