<template>
...
<note-info></note-info>
<note-list></note-list>
...
</template>
I using VueX, store.js
export default {
state: {
noteDataList: [],
},
mutations: {
setNoteDataList: function (state, payload) {
state.noteDataList = payload;
},
}
};
Note List Component:
...
created() {
const note_list = [{id: 1, name: "Monday"},{id: 2, name: "Tuesday"}]
this.$store.commit("setNoteDataList", note_list);
}
...
Note Info Component:
...
computed: {
...mapState(['noteDataList'])
},
mounted() {
console.log(this.$store.state.noteDataList[0]);
},
...
Note info component can't get first object from note list
There's race condition, the specified order of execution cannot be expected from sibling components. They are potentially instantiated in the order they appear in a template, but this isn't guaranteed and shouldn't be relied on.
The logic that is responsible for displaying child components (setNoteDataList, etc) can be moved to parent component, especially if it's synchronous.
Alternatively, the component that accesses noteDataList shouldn't expect it to be ready on instantiation and needs to treat is as reactive value via computed properties or watchers.
Related
I want to access the method in parent component and the method is present in child component. I tried using mixins. But the function is returning null instead of value. If i try to emit the object and that is working fine in parent component. How can I access the child method in parent other in vue3 Options API
Parent.vue
export default defineComponent({
name: 'App',
components: {
HelloWorld
},
mixins: [HelloWorld],
methods: {
fetchval(){
let x = this.getVal();
console.log(x);
}
}
});
</script>
Child.vue
export default defineComponent({
name: 'HelloWorld',
dat(){
let val!: string;
return{
val,
}
},
computed: {
value: {
get() {
return this.val as string;
},
set(newval) {
val = newval;
}
}
},
methods: {
getVal(){
return this.value as string;
}
}
});
</script>
Probably better to rethink the control flow, so that it have a clear separation of concern
A parent should not directly access child's method. If the method should be invoked via a button click, then the child can process the click and emit the event back to parent callabck handler. For details, check this
You may also check out state management, it can achieve what you want. Doc Link
Having said that, declaring a component in the parent and also include that component in parent's mixin doesn't seem common, and may not be a good practice.
Just a quick answer, there might be more way to achieve what you desire.
I have a event emmiter that forces API call in the parent:
Parent component
<ProductChild
:productId="productId"
#update="getProduct()"
:product="product"
></ProductChild>
Child component
props: {
product: {
type: Object,
},
}
methods: {
updateProductTag(){
this.$emit('update')
arait this.productItem = this.product
}
}
It seems the child forces the api call in Parent but I don't get the updated prop in the child. What is wrong and how can I by forcing that method call in parent also pass new prop and use that updated prop in the child?
You need to watch for the prop. There is no warranty that after emit you get the changes immediately.
props: {
reportId: {
type: Number,
},
product: {
type: Object,
},
},
watch:{
product(){
this.productItem = this.product
// after product has updated
}
},
methods: {
updateProductTag(){
this.$emit('update')
// arait this.productItem = this.product
}
}
It is better to do "getProduct()" in the child component and pass the result to the parent by:
this.$emit('update:product', theResultOfGetProduct)
and catch it in the parent by sync:
<ProductChild
:productId="productId"
:product.sync="product"
></ProductChild>
I have empty state in the beginning like so
new Vuex.Store({
state: {
comboBoxNewValues: {}
},
Over time, there are mutations which changes the state like so
this.$store.commit('addComboBoxValues', { input: 'foo', value: ['value': 1, 'name': 'bar']});
Using the following mutation code
mutations: {
addComboBoxValues(state, _value) {
state.comboBoxNewValues[_value.input] = _value['value'];
},
}
It works perfectly and mutations also changes the state, Now I want to perform some action on change of the state so in my component I added a computed property like so
computed: {
getComboBoxNewValues() {
return this.$store.state.comboBoxNewValues;
}
}
When I debug using vue-dev-tools, the components computed property has data. Whenever the data changes using mutation in the vuex, that data is reflected in the computed property as well.
Now I add a watcher to it like so
watch: {
getComboBoxNewValues:{
handler: function(to, from) {
console.log("reload");
},
deep: true
}
}
The problem is that the never never gets called, no matter how many times the data has changed in the computed property. Please advice on what I am missing.
it's about good practice and knowledge on Javascript and VueJs.
I already commented the Type Api to see if other warnings persist
<script>
export default {
name: "printer",
props: {
model: {
type: String,
required: true
},
ip: {
type: String,
required: true
},
jobs: {
type: Object,
validator: (value) => ["inProgress", "done", "error"].every((status) => typeof value[status] === "number")
},
progress: {
type: Number,
required: true
},
api: {
type: Api,
required: true
}
},
data: () => ({
timer: null
}),
methods: {
async refreshProgress() {
this.jobs = await this.api.getJobsNumbers({ model: this.model });
}
},
created() {
this.timer = setInterval(() => this.refreshProgress(), 30000);
},
destroyed() {
clearInterval(this.timer);
}
};
</script>
So I'm looking for advices as I'm a beginner in VueJS and need to describe and purpose changements
Question is missing exact warning text so I'm just guessing the warning you talk about is probably something like this:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "jobs"
In other words you are mutating parent's state in child component which is not a good idea and that's why Vue warns you.
There are several ways how to fix it:
make a copy of props and store it in components data. Update internal state only. Changes will not be visible to parent
Instead of mutating jobs prop, fire a custom event passing new state (returned from API call) as an argument. Parent needs to handle the event and update its own state with new value (update will flow through prop back to child component)
If you are absolutely sure your printer component will be the only place where jobs object will be mutated, you can mutate object's properties instead of replacing whole object without any warnings. This is possible but not recommended...
For more see Vue documentation or some very related SO questions
I'm having trouble understanding how to interact with my local state from my vuex state. I have an array with multiple items inside of it that is stored in vuex state. I'm trying to get that data from my vuex state into my components local state. I have no problems fetching the data with a getter and computed property but I cannot get the same data from the computed property into local state to manipulate it. My end goal is to build pagination on this component.
I can get the data using a getters and computed properties. I feel like I should be using a lifecycle hook somewhere.
Retrieving Data
App.vue:
I'm attempting to pull the data before any components load. This seems to have no effect versus having a created lifecycle hook on the component itself.
export default {
name: "App",
components: {},
data() {
return {
//
};
},
mounted() {
this.$store.dispatch("retrieveSnippets");
}
};
State:
This is a module store/modules/snippets.js
const state = {
snippets: []
}
const mutations = {
SET_SNIPPETS(state, payload) {
state.snippets = payload;
},
}
const actions = {
retrieveSnippets(context) {
const userId = firebase.auth().currentUser.uid;
db.collection("projects")
.where("person", "==", userId)
.orderBy("title", "desc")
.onSnapshot(snap => {
let tempSnippets = [];
snap.forEach(doc => {
tempSnippets.push({
id: doc.id,
title: doc.data().title,
description: doc.data().description,
code: doc.data().code,
person: doc.data().person
});
});
context.commit("SET_SNIPPETS", tempSnippets);
});
}
}
const getters = {
getCurrentSnippet(state) {
return state.snippet;
},
Inside Component
data() {
return {
visibleSnippets: [],
}
}
computed: {
stateSnippets() {
return this.$store.getters.allSnippets;
}
}
HTML:
you can see that i'm looping through the array that is returned by stateSnippets in my html because the computed property is bound. If i remove this and try to loop through my local state, the computed property doesn't work anymore.
<v-flex xs4 md4 lg4>
<v-card v-for="snippet in stateSnippets" :key="snippet.id">
<v-card-title v-on:click="snippetDetail(snippet)">{{ snippet.title }}</v-card-title>
</v-card>
</v-flex>
My goal would be to get the array that is returned from stateSnippets into the local data property of visibleSnippets. This would allow me to build pagination and manipulate this potentially very long array into something shorter.
You can get the state into your template in many ways, and all will be reactive.
Directly In Template
<div>{{$store.state.myValue}}</div>
<div v-html='$store.state.myValue'></div>
Using computed
<div>{{myValue}}</div>
computed: {
myValue() { return this.$store.state.myValue }
}
Using the Vuex mapState helper
<div>{{myValue}}</div>
computed: {
...mapState(['myValue'])
}
You can also use getters instead of accessing the state directly.
The de-facto approach is to use mapGetters and mapState, and then access the Vuex data using the local component.
Using Composition API
<div>{{myValue}}</div>
setup() {
// You can also get state directly instead of relying on instance.
const currentInstance = getCurrentInstance()
const myValue = computed(()=>{
// Access state directly or use getter
return currentInstance.proxy.$store.state.myValue
})
// If not using Vue3 <script setup>
return {
myValue
}
}
I guess you are getting how Flux/Vuex works completely wrong. Flux and its implementation in Vuex is one way flow. So your component gets data from store via mapState or mapGetters. This is one way so then you dispatch actions form within the component that in the end commit. Commits are the only way of modifying the store state. After store state has changed, your component will immediately react to its changes with latest data in the state.
Note: if you only want the first 5 elements you just need to slice the data from the store. You can do it in 2 different ways:
1 - Create a getter.
getters: {
firstFiveSnipets: state => {
return state.snipets.slice(0, 5);
}
}
2 - Create a computed property from the mapState.
computed: {
...mapState(['allSnipets']),
firstFiveSnipets() {
return this.allSnipets.slice(0, 5);
}
}