I have a problem with iterating over mapGetters results.
My code looks like :
...mapGetters({
'shop' : 'getShops'
})
After that, when I iterate over the shops and change anything, it changes my parameters in state and it has impact on all of my app state. I need to change parameters on 'copy' of this getters but it also should be observable.
I've tried to assign mapGetters result to a computed variable but it also updated state
How can I achieve this?
here is a typical store getter:
const state = {
obj: {count:1}
}
const getters = {
obj: (state) => {
return state.obj
}
}
if you call a store by either mapGetters or directly through $store.state.obj, you are getting the object itself passed. The object javascritp provides is not immutable, and the nature of objects in js is such that if you make a change to the object when it's passed to a component, it will update in the store too.
There are two ways you can prevent objects not updating through the passed getter.
do not make changes to it EVER, just reference or copy values as needed when changes are needed.
return a copy of the object in the getter
I've personally only used approach 1, but if you need to return a copy for changing (2), you can use any one of these to create a copy
using ES6 spread operator (not a deep copy)
objCopy: (state) => {
return {...state.obj};
}
also a good option for ES6 (not a deep copy):
objCopy: (state) => {
return Object.assign({}, state.obj});
}
or the ES5 way, which creates a deep copy (so the children, grandchildren, etc don't succumb to same issue)
objCopy: (state) => {
return JSON.parse(JSON.stringify(state.obj));
}
Related
I prefer to move functionality outside Components when the Component code becomes to crowded. So, this is a problem with which I have confronted several times when I have passed Component Props as arguments to functions that were outside of the Component scope, hosted in a different file as helper functions or as class methods. The mistake was fed also by the personal idea that I should use imutable objects everywere for better performance.
// the component file hosted in "MyComponent.vue"
<template>
<button #click="update">Please, update!</button>
</template>
<script>
import {
updatePrimitiveFn,
updateLiteralObjectFn,
updateArrayFn,
} from "../helpers.js";
export default {
name: "MyComponent",
data() {
return {
primitiveToBeUpdated: false,
arrayToBeUpdated: [],
literalObjectToBeUpdated: {},
};
},
methods: {
update() {
updatePrimitiveFn(this.primitiveToBeUpdated);
updatedLiteralObjectFn(this.literalObjectToBeUpdated);
updateArrayFn(this.arrayToBeUpdated);
console.log("primitiveToBeUpdated", this.primitiveToBeUpdated);
console.log("literalObjectToBeUpdated", this.literalObjectToBeUpdated);
console.log("arrayToBeUpdated", this.arrayToBeUpdated);
},
},
};
</script>
// the helpers functions hosted in "helpers.js"
export function updatePrimitiveFn(primitiveParameter) {
primitiveParameter = true; // it will not trigger reactivity, the argument it's a primitive and for primitives JavaScript copies the argument value to the formal parameter. For arguments that are objects, JavaScript copies the pointers of these objects to the formal parameter, keeping the link between component props and formal parameters. Pointers stores the memory address where some data lives.
}
export function updateLiteralObjectFn(literalObjectParameter) {
literalObjectParameter = { value: true }; // it will not trigger reactivity, it replaces the pointer of the received argument with a fresh new one.
}
export function updateArrayFn(arrayToBeUpdated) {
arrayToBeUpdated = [true]; // it will not trigger reactivity, it replaces the pointer of the received argument with a fresh new one.
}
The solution is to pass only objects, as arguments, to the external functions and mutate them as follows:
// the helpers functions hosted in "helpers.js"
export function updatePrimitiveFn(primitiveParameter) {
primitiveParameter = true; // it will not trigger reactivity, the argument it's a primitive and for primitives JavaScript copies the argument value to the formal parameter. For arguments that are objects, JavaScript copies the pointers of these objects to the formal parameter, keeping the link between component props and formal parameters. Pointers stores the memory address where some data lives.
}
export function updateLiteralObjectFn(literalObjectParameter) {
literalObjectParameter.value = true; // mutation keeps pointing same object in memory
}
export function updateArrayFn(arrayToBeUpdated) {
arrayToBeUpdated.push(true); mutation keeps pointing same object in memory
}
Check live demo: https://codesandbox.io/s/hardcore-volhard-mf6x0?file=/src/components/MyComponent.vue:0-179
For more information can be studied the subject "Pass by Value vs. Pass by Reference".
For example, I use a Getter for write a logic about a if object has a especify content.
const state = {
object = []
}
const getters = {
CHECK_OBJECT_STATE: (state) => {
if(Object.prototype.hasOwnProperty.call(state.object, "error")){
//return a message, in this case is not necessary proceed the logic.
}else{
//otherwise I need take state.object.id and fetch a new a action.
//CALL ACTION FETCH_OBJECT_ID
}
}
}
const actions = {
FETCH_OBJECT(...){
//SET MUTATION HERE
}
FETCH_OBJECT_ID (..) {..}
}
anyone can suggest me other method to do it, if it's not valid?
Vuex Getters are not designed for asynchronous tasks, those should be handled in the actions. I really think that getters operate best as pure functions which perform read-only operations on an existing state, so it should do only one job: retrieve the data in the state.
So you process the logic, the if statement in the actions, store the returned value in a state array object (which is pretty weird for me to name an array "object"), and the getter only has to retrieve such array. Things are reactive so that getter will always hold the latest data set.
In several Vue files this computed property checks if this.data is not an empty object:
computed: {
isLoaded() {
return !(this.data && Object.keys(this.data).length === 0 && this.data.constructor === Object); // checks if this.data is not empty
}
}
Then isLoaded is used to conditionally display content in the browser.
I'd like to refactor the code and create a global method somehow that can check if an object is empty so all the files that use this method can get it from a central spot.
Even after doing some reading on Vue mixins and plugins I'm not clear which one best fits this use case. Which one should be used for this? Or is there an altogether different approach that would be better to create a global method?
There are several ways to do it and it and approach depends on particular case. For your case I'd suggest you to create a separate folder for utils functions, where you could have smth like common.js. There you can just export your functions e.g.
export const emptyObj = (obj: any): any => Object.keys(obj).length === 0;
and import it in your component:
import { emptyObj } from "src/utils/common";
In this case it easier to organize your shared functions, easier to test them and have typescript support.
I am trying to use Vuex and am having trouble to understand why my data is not the expected value.
What I am trying to do is pass the result of a getter to a mutation commit as follows
// TEST PRINT: return value of getter
console.log(this.$store.getters.reservationsCurrentWeek);
switch (this.activeCalendar) {
case 'week':
// Commit with getter result
this.$store.commit('setShownReservations', this.$store.getters.reservationsCurrentWeek);
break;
The first print shows the expected result.
However when I print what value is actually passed inside the mutation as follows:
// TEST PRINT: return value of getter
console.log(this.getters.reservationsCurrentWeek);
// TEST PRINT: parameter passed to the mutation.
console.log(value);
state.shownReservations = value;
The parameter value is is not what the getter returned (but some old value). When I access the getter directly in my mutation it also gives a correct value. I don't get why I can't pass the result of the the getter as a parameter to a mutation commit and why it produces an unreliable value?
From where are you trying to access this.getters.reservationsCurrentWeek ? If you are accessing getters from mutation and getters and mutations placed in one file you can use
mutations: {
YOUR_MUTATION(state, {data, getters}) {
console.log(getters);
}
}
and then access getters. Or import getters from another file if it placed in separate file.
I'm wondering why do we write the function name of mutations, actions and getters in uppercase? Where does this convention come from?
export default {
SOME_MUTATION (state, payload) {
},
ANOTHER_MUTATION (state, payload) {
},
}
The accepted answer by Bert is a bit misleading. Constant variables are traditionally written in all caps, but the way it is used in the question does not make it a constant.
This allows the code to take advantage of tooling like linters
The official Vue.js documentation recommends using all caps, but as variables in an additional file. This makes it possible to require the available function names in other files and use auto-complete.
mutation-types.js:
export const SOME_MUTATION = 'SOME_MUTATION'
store.js:
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'
const store = new Vuex.Store({
state: { ... },
mutations: {
// we can use the ES2015 computed property name feature
// to use a constant as the function name
[SOME_MUTATION] (state) {
// mutate state
}
}
})
Please note the different writing-style here (computed property name with square brackets):
[SOME_MUTATION] (state) { }
If you just write the function names in all upper case (i.e. SOME_MUTATION(state) { }) the only benefit is a mere visual one (to separate Vuex functions from others), but in my opinion this does not make much sense. Stick to the one with computed property name ([SOME_MUTATION] (state)) to get all the benefits.
It is a long standing coding style to write constants in all uppercase.
From the Vuex documentation:
It is a commonly seen pattern to use constants for mutation types in
various Flux implementations. This allows the code to take advantage
of tooling like linters, and putting all constants in a single file
allows your collaborators to get an at-a-glance view of what mutations
are possible in the entire application
So, it's really just following a long standing tradition of naming constants in uppercase for the most part. It's not required.
Whether to use constants is largely a preference - it can be helpful in large projects with many developers, but it's totally optional if you don't like them