Vuex use keys to access object in state - vue.js

I have code like this for a store in Vuex, if do it like the code below it works fine.
state: {
APIData: {},
},
getters: {
getFeed: state => {return state.APIData },
},
mutations: {
SET_FEED_DATA(state, {folder_id, data}) {
state.APIData = data
}
},
However, if I try to use a key to set and get the value it return undefined in the getter. I want to do this because I have a for loop in my template calling an action multiple times but with different id's that I will use here for the key value.
state: {
APIData: {},
},
getters: {
getFeed: state => {return state.APIData["test"] },
},
mutations: {
SET_FEED_DATA(state, {folder_id, data}) {
state.APIData["test"] = data
}
},
Edit
Looking in Vue debugger I find the following, so it seems the mutation is setting that data but the getter can access it.

Inside the mutation, try to set it like below:
state.APIData = { ...state.APIData, test: data };
The getter is for APIData and it will not recognize the change if you update an individual property directly. You need to reset the whole APIData object.
To replace the test with a variable:
state.APIData = { ...state.APIData, [VARIABLE_NAME]: data };
For example, if you want to pass the key name as folder_id in your code:
state.APIData = { ...state.APIData, [folder_id]: data };

Related

How pass state vuex on created()

I have been using vuex for a project for a few days now. Now I need to pass the value of a state to the created method.
Below I show the code used.
Store file
state: {
friendstatus: null
},
mutations: {
SET_FRIEND_STATUS: (state,friend) => {
state.friendstatus = data;
},
actions: {
getFriendStatus: ({commit},data) => {
//axios request returns friendstatus
commit('STATE_FRIEND_STATUS',response.data.status)
}
Vue component
computed: {
...mapstate('friend',['friendstatus'])
},
created() {
//Here I need to pass friendstatus. Obviously if I call this.friendstatus it does not work.
this.$store.dispatch('friend/getFriendStatus);// I would like to call the state and not the action
}

Getter in Vues not updating

I have a module in my VUex state
mutations: {
appendTasks(state, tasks) {
tasks.forEach((task) => {
state.tasks[task.id] = task;
});
},
},
state: {
tasks: {},
},
getters: {
getTasks(state) {
return Object.values(state.tasks);
},
getTasksCount(state) {
return Object.keys(state.tasks).length;
},
},
I want to store tasks as dictionary because I need to access them by id. Also I need a method to get a list if tasks. But getTasks doesn't work for me. And getTasks returns an empty Array Why?
Here is the explanation of my problem and how to solve it
Just use Vue.set()
https://v2.vuejs.org/v2/guide/reactivity.html#For-Objects

Vuex: Add Dynamic Property to Object without triggering Watchers on existing Properties

I have a Vuex store with an object:
state: {
contents: {},
}
where I dynamically store contents by key:
mutations: {
updateContent: (state, { id, a, b }) => {
Vue.set(state.contents, id, { a, b });
},
}
and get them using:
getters: {
content: (state) => (id) => {
if (id in state.contents) return state.contents[id];
return [];
}
},
Let's say I have a component like this:
export default {
props: ["id"],
computed: {
myContent() {
return this.$store.getters.content(this.id)
}
},
// ...
}
How can I add dynamic properties using the mutation without triggering changes in components watching unchanged, already existant properties of state.contents?
Also see this fiddle.
If you want to watch inner property of objects, you can use deep watchers.
In your situation, i am assuming you're setting properly yor getters, setter and update methods. You should add this to your watchers:
// ...
watch:{
id: {
deep: true,
handler(newVal, oldVal){
console.log("New value and old value: ", newVal, oldVal")
// ... make your own logic here
}
}
}
Let me explain little bit more above code, when we want to watch inner property of any object in Vue, we should use deep and handler function to manipulate in every change. (remember that, handler is not a random name, it's reserved keyword)
I'm trying to figure it out by checking the fiddle: https://jsfiddle.net/ericskaliks/Ln0uws9m/15/ , and I have a possible reason to this behavior.
Getters and Computed properties are updated or reached when the observed objects or properties inside them are changed. In this case the content getter is "watching" the state.contents store property, so each time store.contents is updated, the content getter is called, then the computed property myContent() updates its value and increase the updated data property with this.update++.
So the components will always be updated if the state.contents property is updated, i.e. adding a new unrelated property.
One could use a global event bus instead of a vuex getter:
const eventBus = new Vue();
components can subscribe to the element they need:
watch: {
id: {
handler(n, o) {
if (o) eventBus.$off(o + "", this.onChange);
eventBus.$on(n + "", this.onChange);
this.$store.dispatch("request", { id: n });
},
immediate: true
}
},
and changes of which the components have to be notified are dispatched using an action:
actions: {
request(_, { id }) {
eventBus.$emit(id + "", this.getters.content(id));
},
updateContent({ commit }, { id, a, b }) {
commit("updateContent", { id, a, b });
eventBus.$emit(id + "", { a, b });
}
}
That way one can precisely control when which updates are fired.
fiddle.
It seems like Vue(x) can't do this out-of-the-box. But by adding an empty Observer (yes, this is a hack) you can make Vue-components temporarily nonreactive (based on this blogpost).
Basically - we have an Observer on the object itself and an Observer on each of the properties. We will destroy the Observer on the object. When doing so, we have to make sure, that when the getter is first called by a component it returns a reactive value rather then {}, because the getter can't observe adding a new property to the object anymore. Therefore, we add a touch-mutation initializing the object. This function needs the original Observer of the object to create Observers on its properties, causing one, but only one, unnecessary update of the component:
mutations: {
updateContent: (state, { id, a, b }) => {
Vue.set(state.contents, id, { a, b });
},
touch: (state, { id }) => {
if(id in state.contents) return
if (myObserver === null)
myObserver = state.contents.__ob__
state.contents.__ob__ = myObserver
Vue.set(state.contents, id, {});
state.contents.__ob__ = new Observer({});
}
},
The constructor can be obtained using:
const Observer = (new Vue()).$data.__ob__.constructor;
Our component has to call touch whenever the id changes:
props: ["id"],
watch: {
i: {
immediate: true,
handler() {
this.$store.commit("touch", { id: this.id })
}
}
},
computed: {
myContent() {
return this.$store.getters.content(this.id)
}
},
As you can see in this fiddle, adding new properties to the object doesn't trigger unnecessary updates anymore.

How to get value from state as my vuex getter is returning empty value in observer

Getter is returning empty value in observer. But the state is setting properly in the mutation.
Not able to check in Vuex dev tools in console as it says "No Store Detected". I've checked it by logging it in console
Vue File :
computed: {
...mapGetters('listings', ['listingContracts']),
},
methods: {
...mapActions('listings', [
'productBasedListings',
]),
},
onChange(product) {
this.productBasedListings( product.id );
console.log('LIST:', this.listingContracts); // Empty in observer
},
Store :
state: {
contracts: [],
},
getters: {
listingContracts(state) {
console.log('GETTER', state.contracts); // Empty in observer
return state.contracts;
},
},
mutations: {
setListing(state, { lists }) {
state.contracts = lists;
console.log('AFTER MUTATION:', state.contracts); // Setting the value properly
},
},
actions: {
async productBasedListings({ commit }, { id, state }) {
let listing = [];
try {
listing = await publicApi.listings(id);
console.log('ACTION:', listing);
commit({
lists: listing,
type: 'setListing',
});
} catch (e) {
console.error(`Failed to change #${id} state to #${state}:\t`, e);
throw e;
}
},
}
Here "Getter" does not have any values but "After Mutation" we have the values.
Because initially the store variable is empty.The values are itself set in the mutation.Hence showing up after mutation is called.
Well now to get data after mutation is fired use async await in your method as below:
async onChange(product) {
await this.productBasedListings( product.id ).then(() => {
console.log('LIST:', this.listingContracts);
})
},

Using Vue-Resource in Vuex store; getting maximum call stack size error

I'm trying to pull an array from my api to a component using vuex but I'm at a loss and probably in over my head in attempting this. With the api being accessed directly from a component I had it set up like this and it was fine:
data () {
return {
catalog:[],
}
},
created() {
this.$http.get('https://example.net/api.json').then(data => {
this.catalog = data.body[0].threads;
})
}
For reference the json looks similar to this:
[{
"threads": [{
"no: 12345,
"comment": "comment here",
"name": "user"
}, {
"no: 67890,
"comment": "another comment here",
"name": "user2"
}],
//this goes on for about 15 objects more in the array
"page": 0
}]
When I move this all to store I'm losing grasp on how to actually make this work. I've used vuex before just never with vue-resource.
//store.js
state: {
catalog: []
},
actions: {
getCatalog({commit}){
Vue.http.get('https://example.net/api.json').then(response => {
commit('LOAD_CATALOG', response.data.data)
});
}
},
mutations: {
LOAD_CATALOG (state) {
state.catalog.push(state.catalog)
}
},
getters: {
catalog: state => state.catalog,
}
//component.vue
created () {
this.$store.dispatch('getCatalog')
},
computed: {
catalog () {
return this.$store.getters.catalog
}
}
I'm aware this is wrong and I'm getting max call stack size errors. How can I get the same results as posted in the example above (this.catalog = data.body[0].threads;) when I put everything in store?
Let me know if anything needs clarification! I'm still pretty new at using Vue 2.0.
Your main issue is with your mutation.
Mutations are synchronous updates to the state, therefore you are correctly calling it from the action (where you process your async request) but you aren't passing the mutation anything to place in the state. Mutations accept arguments, so your LOAD_CATALOG mutation would accept the catalogData, i.e.
mutations: {
LOAD_CATALOG (state, catalogData) {
state.catalog = catalogData
}
},
Also if you are using vue resource for Vue 2 then you should be passing the body of the response to the mutation, i.e.
getCatalog({commit}){
Vue.http.get('https://example.net/api.json').then(response => {
commit('LOAD_CATALOG', response.body[0].threads)
});
}
The next issue is you don't need the getter, getters allow us to compute a derived state, you don't need them just to return an existing state (in your case catalog). A basic example of where you may use a getter would be to add 1 to a counter stored in state, i.e.
getters: {
counterAdded: state => state.counter + 1,
}
Once you've made these changes things will look a bit more like below:
//store.js
state: {
catalog: []
},
actions: {
getCatalog({commit}){
Vue.http.get('https://example.net/api.json').then(response => {
commit('LOAD_CATALOG', response.body[0].threads)
});
}
},
mutations: {
LOAD_CATALOG (state, catalogData) {
state.catalog = catalogData
}
},
//component.vue
created () {
this.$store.dispatch('getCatalog')
},
computed: {
catalog () {
return this.$store.state.catalog
}
}