Good morning everyone.
I have been struggling for several days on an app I'm trying to build for some experience. I have done quite a lot but am now stuck in the edit page as I just cannot grasp the state management side.
So here is my problem.
I have a button in my ProfilePage.vue that if I click on it sends me to the EditInvoice.vue page.
<button #click="onSubmit"><router-link to="/edit-invoice">Edit</router-link></button>
my store.js state:
state: {
invoice: [],
},
Then in my store.js, I have the following in my actions:
actions: {
invoiceCollection({commit}) {
database.collection('invoices')
.get()
.then((querySnapShot) => {
querySnapShot.forEach((doc) => {
const curInvData = doc.data();
commit('invoice', curInvData);
})
})
}
},
This action gets the data I need from firestore and should look like this.
clientDetails: "Adress"
dateCreated: "September 15th 2019"
invoice: Array(2)
invoiceSubTotal: "R 167,50"
invoiceTotal: (...)
itemPaid: (...)
userId: (...)
userName: (...)
I then mutate my state (store.js):
mutations: {
invoice: (state, payload) => state.invoice = payload,
},
and then use a getter (store.js):
getters: {
// Get Invoice data from state
invoice: state => {
return state.invoice
},
},
I then import mapGetters into my component (EditInvoice.vue) ...iterate through my getter's under my computed property with ...mapGetters(['invoice']),
and then use a simple function with a console log and use a lifecycle hook.
created() {
this.currentInvoice();
},
methods: {
...mapActions(['invoiceCollection']),
currentInvoice() {
console.log(this.invoice)
},
I'm very new to programming and would just like to know, why my getters, and everything else always returns an empty Observer
[__ob__: Observer]
length: 0
__ob__: Observer {value: Array(0), dep: Dep, vmCount: 0}
__proto__: Array
on the first few attempts. And then after a few clicks on the edit button in ProfilePage.vue eventually shows the right data
I have been searching for the same issue and have found several cases but none have helped me. This is a simplified version that I stripped. All I want to know is why my state is not persistent. The data is there it's just not showing on the first or second-page load.
Any help will be greatly appreciated.
You are using this.currentInvoice(); in created method, created method run when component is created (not even mounted) so nothing has run, try running this.currentInvoice(); in mounted or beforeUpdate life cycle method.
Also go through this: Vue Js Life Cycle Methods
Related
I have an object that when logged prints the following:
Object {
"Air Conditioning": false,
"Attic": false,
"Basement": false,
"Bathrooms": false,
"Bedrooms / Living Areas": false,
"Crawl Space": false,
}
I would like to setState using the above. I attempted the following:
componentDidMount() {
this.setAreaNamesInState(this.props.areas)
}
setAreaNamesInState(areaNames) {
let areaNamesList = {}
for (let area of areaNames) {
areaNamesList[area] = false
}
console.log('areaNamesList', areaNamesList)
this.setState(areaNamesList)
console.log('Attic', this.state['Attic'])
}
It doesn't seem to be working, as when I log Attic above it returns undefined.
The answers of other users are correct, you could do the following
this.setState({ areas: areaNamesList }, () => {
console.log('Attic', this.state.areas['Attic'])
})
The difference is you are trying to set the whole state object with your newly created object, which is a bad practice, while this answer updates a property called areas inside your state object with the provided data.
The console log will execute synchronously after the state is updated with the property areas, and log false
As a side note, maybe using componentDidMount is a bad idea if the prop is not provided the first time the component is created and rendered, since it's only executed the first time and never again, the property might be undefined by the time you need it.
Consider switching to other lifecycle methods.
Try with
this.setState({ areaNamesList }, () => {
console.log('Attic', this.state.areaNamesList['Attic'])
})
You're missing curly braces in your setState as this.state is an object.
this.setState({ areaNamesList })
Also worth mentioning that setState may not be completed before console.log('Attic', this.state.areaNamesList['Attic']). setState can take a callback that will be executed when it is complete as such:
this.setState({ Attic: areaNamesList }, () => {
console.log('Attic', this.state.areaNamesList["Attic"])
})
I have been using Vuex to control my whole app state for a while. But now I am facing a problem I have never meet before.
The Workflow is like this:
The root component will fetch data from my database, which is an array with multiple objects in it.
I created child-component and use v-for to show this array to user (that means, each child-component represents an object in the array later)
The Problem comes, when I try to fetch async data for each child-component, for the data-fetching, I also need parameter which come from the array I mentioned.
I use the Vuex actions in the created hook of my child components. The actions fetch_sim_Type will take the payload (which is parameter from parent-component, also from the initial array). And change the state simType in the state: {} of Vuex.
By using the computed properties I can get the fetched simType in my child-component and show it to the user. Everything works so far.
CODES:
THE initial Array (simSumArray in Parent-component) looks like this:
[
{ typeid: 1, name: XXX },
{ typeid: 2, name: ZZZ },
{ typeid: 3, name: GGG },
{ typeid: 4, name: JJJ },
]
PARENT-COMPONENT:
<template
v-for="(singleSim, idx) in simSumArray"
>
<sim-single :singleSim="singleSim"></sim-single>
</template>
CHILD-COMPONENTS:
props: ['singleSim'],
created () {
store.dispatch('fetch_sim_Type', this.singleSim.typeid);
},
computed: {
simType () {
console.log("store.getters.get_simType: ", store.getters.get_simType)
return store.getters.get_simType;
}
},
IN VUEX:
state: {
simType: 'unknown'
},
actions: {
fetch_sim_Type (context, typeid) {
//.. fetch the data based on typeid from DB
context.state.simType = fetchedData;
}
}
But it only works when in the array only when object exist. When there are more than one child-component being created. The state simType in the Vuex store.js will be replaced many times and in every child-component the simType() is always the same.
The Problem is kind of hard to describe. The central problem is, the state in Vuex is meant to be shared everywhere in the whole app, so if I have multiple child-components, they all fetch data for themself, than the shared state will be replaced all the time and I can't get individual state for every child-components.
I don't know if I describe the problem clair but I really tried hard to.
Maybe There is a better way to do this data fetching job without Vuex or maybe I just used the Vuex by the wrong way.
I am sure this should not be a hard question. But I can't find any relative answer online.
Reading your code, the behaviour you describe is normal. I see two solution to your problem (solution 2 is probably closer to what you are looking for) :
Solution 1 - store simType in your component
if you need to access the simType from somewhere else than inside your component and have it stored in your state, skip to solution 2
When your component is created, store the simtype in the component's data. This would look like this:
In your component:
data () {
return {
simType: undefined //declare simType as one of your component's data property
}
},
created () {
store.dispatch('fetch_sim_Type', this.singleSim.typeid).then(simType => {
this.simType = simType //store the fetched simType
})
}
In your vuex Actions:
actions: {
fetch_sim_Type (context, typeid) {
//.. fetch the data based on typeid from DB
return fetchedData //pretty much the same except return the result
}
}
Solution 2 - store simTypes in your state indexed by their IDs
Store your fetched simType by id, like this:
state: {
simTypes: {} //simTypes is now plural, and is an empty object at first. It will later contain several simTypes, indexed by their respective Ids
},
actions: {
fetch_sim_Type (context, typeid) {
//.. fetch the data based on typeid from DB
context.state.simType[typeid] = fetchedData; // a fetched simtyped will be store in simTypes and can be accessed with the typeid as a key
}
}
to retrieve a simType, you can write a vuex getter like this:
getters: {
getSimTypeById: (state) => (typeId) => {
return state.simTypes[typeId]
}
}
So in your example, the computed method would be :
computed: {
simType () {
console.log("store.getters.getSimTypeById(this.singleSim.typeid): ", store.getters.getSimTypeById(this.singleSim.typeid)
return store.getters.getSimTypeById(this.singleSim.typeid);
}
},
This solution, as a bonus, allows you to fetch a simType only once if several of your items have the same simType.
I have had success by keeping shared data in the Vuex store, and watching it from my components.
Although not a best practice, I sometimes don't even bother to use actions or commits to change the state, and just modify the state directly. In this scenario, Vuex just acts like a shared data object for all my components.
Vue Store
state: {
myvalue: []
}
Components
watch: {
'$store.state.myvalue'(value) {
}
}
I have a list of todos that I'd like to watch and auto update when a change is made in a firebase backend
Got a bit stuck on what i hope is the last step (I am using nuxtjs for a SPA)
I have a getter in my vuex store as follows
getMyState: state => {
return state.todos
}
I am returning this getter to my component as follows
computed: {
...mapGetters(['getMyState'])
},
How do i now get my list to recognise when something has changed and update my array of results?
Thanks
Use watch property
watch: {
getMyState: function (new, old) {
}
},
I'm using computed to copy my prop value and use/mutate it in my component:
export default {
props: ['propOffer'],
computed: {
offer: {
get: function () {
return JSON.parse(JSON.stringify(this.propOffer))
},
set: function () {
this.offer
}
},
}
The problem is within using setter. It is not reactive. When I use some kind of input, there is a delay, so my computed offer isn't updating instantly. Example of input:
<v-text-field
label="Offer title"
v-model="offer.title"
></v-text-field>
This is far opposite to the behaviour when I declare offer as a variable (wthout computed) - then I got my {{offer}} changes instantly inside the <template>
How can I improve it? Am I setting my computed wrong?
To better understand this situation, this is what happens at the moment:
When the application loads, the initial state is:
<your-component>
propOffer: '{"title":"test"}'
offer.<lastValue>: undefined
At the point in time, your application will load the v-text-field, this references field offer, and this inits the offer computed variable:
<your-component>
propOffer: '{"title":"test"}'
offer.<lastValue>: [Javascript object 1]
[Javascript object 1]
title: "test"
<v-text-field>
value: "test"
As the user types into the v-text-field, its value changes, because the v-model emits back updates:
<your-component>
propOffer: '{"title":"test"}'
offer.<lastValue>: [Javascript object 1]
[Javascript object 1]
title: "test123"
<v-text-field>
value: "test123"
As you can see here, the setter is never invoked in the normal operation, and hence your code to save it does not run.
You can solve this by making another computed prop for the title of the offer, and then adding some code to prevent your changes from being made undone.
Let's start with the getter & setter for the title:
computed: {
title: {
get() {
return this.offer.title;
},
set(title) {
this.offer = {...this.offer, title};
}
},
// ....
Now we need to properly handle this set operation inside our main offer function, because if we don't handle it, and basically modify its returned object, we get into the territory of undefined behaviour, as the value of the computation doesn't match the computation.
// ...
offer: {
get: function () {
if (this.modifiedOffer) {
return this.modifiedOffer;
}
return JSON.parse(JSON.stringify(this.propOffer))
},
set: function (offer) {
this.modifiedOffer = offer;
}
},
},
data() {
return: {
modifiedOffer: undefined,
};
},
After doing this pattern, you now have a stable application, that shows no undefined behaviour, for more functionality, you basicly need to check if the propOffer changes, and either forcefully delete the this.modifiedOffer, or add more logic to a different computed variable that informs the user there is a data conflict, and ask him to overwrite his data.
I have a Vuex store with the following state:
state: {
authed: false,
id: false
}
Inside a component I want to watch for changes to the authed state and send an AJAX call to the server. It needs to be done in various components.
I tried using store.watch(), but that fires when either id or authed changes. I also noticed, it's different from vm.$watch in that you can't specify a property. When i tried to do this:
store.watch('authed', function(newValue, oldValue){
//some code
});
I got this error:
[vuex] store.watch only accepts a function.
Any help is appreciated!
Just set a getter for the authed state in your component and watch that local getter:
watch: {
'authed': function () {
...
}
}
Or you can use ...
let suscribe = store.subscribe((mutation, state) => {
console.log(mutation.type)
console.log(mutation.payload)
})
// call suscribe() for unsuscribe
https://vuex.vuejs.org/api/#subscribe