I have a add-user.vue
component. It is my template for adding a new user and editing a existing user. So on page load I check if route has a id
, if so I load a user from a state array to edit it. My issue is that user
is undefined because the state array users
is empty. How can I ensure that my user object isn't undefined. It does load sometimes but on refresh it doesn't. I thought I had it covered but nope. This is my setup. What am I missing here?
Store
state: {
users: []
},
getters: {
users: state =>
_.keyBy(state.users.filter(user => user.deleted === false), 'id')
},
actions: {
fetchUsers({
commit
}) {
axios
.get('http://localhost:3000/users')
.then(response => {
commit('setUsers', response.data);
})
.catch(error => {
console.log('error:', error);
});
}
}
In my add-user.vue
component I have the following in the data()
computed:{}
and created()
data() {
return {
user: {
id: undefined,
name: undefined,
gender: undefined
}
};
},
computed: {
...mapGetters(['users'])
},
created() {
if (this.$route.params.userId === undefined) {
this.user.id = uuid();
...
} else {
this.user = this.users[this.$route.params.userId];
}
}
Template
<template>
<div class="add-user" v-if="user"></div>
</template>
My User.vue
I have the following setup, where I init the fetch of users on created()
<template>
<main class="main">
<AddUser/>
<UserList/>
</main>
</template>
<script>
import AddUser from '@/components/add-user.vue';
import UserList from '@/components/user-list.vue';
export default {
name: 'User',
components: {
AddUser,
UserList
},
created() {
this.$store.dispatch('fetchUsers');
}
};
</script>
I have tried this. When saving in my editor it works but not on refresh. The dispatch().then()
run before the mutation
setting the users does.
created() {
if (this.$route.params.userId === undefined) {
this.user.id = uuid();
...
} else {
if (this.users.length > 0) {
this.user = this.users[this.$route.params.userId];
} else {
this.$store.dispatch('fetchUsers').then(() => {
this.user = this.users[this.$route.params.userId];
});
}
}
}
Vuex allows us to define "getters" in the store. You can think of them as computed properties for stores. As of Vue 3.0, the getter's result is not cached as the computed property does.
Mapping in Vuex enables you to bind any of the state's properties, like getters, mutations, actions, or state, to a computed property in a component and use data directly from the state. Although we can get the job done with this. $store.state.user.data.name , we can use a map helper to simplify it to this.
Compared to Vuex, Pinia provides a simpler API with less ceremony, offers Composition-API-style APIs, and most importantly, has solid type inference support when used with TypeScript.
Mutations are intended to receive input only via their payload and to not produce side effects elsewhere. While actions get a full context to work with, mutations only have the state and the payload .
I would use beforeRouteEnter
in User.vue
so that the component is not initialized before the data is loaded.
(Assuming you are using vue-router
)
beforeRouteEnter (to, from, next) {
if (store.state.users.length === 0) {
store.dispatch(fetchUsers)
.then(next);
}
},
You'll need to
import store from 'path/to/your/store'
because this.$store
is not available until the component is initialized.
Although this solved, but I will answer for future comers. You may issue the dispatch
beforehand as Shu suggested, or you may still dispatch on same component mounted
hook, but use some state variable to track the progress:
data:{
...
loading:false,
...
},
...
mounted(){
this.loading = true,
this.$store
.dispatch('fetchUsers')
.finally(() => (this.loading=false));
}
Then in your template you use this loading
state variable to either render the page or to render some spinner or progress bar:
<template>
<div class='main' v-if="!loading">
...all old template goes her
</div>
<div class="overlay" v-else>
Loading...
</div>
</template>
<style scoped>
.overlay {
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
color: #FFFFFF;
}
</style>
For this particular case it was just going back and forth to the same vue instance. Solved it by adding :key="some-unique-key"
, so it looks like this.
<template>
<main class="main">
<AddUser :key="$route.params.userId"/>
<UserList/>
</main>
</template>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With