Vuex strict mode throws exception while updating nested array - vue.js

I have a page where user can edit multiple segments. Every segment has a name and an array of filters that he can added/removed.
{
segments: [
{
name: 'Mac',
filters: [
{
field: 'os',
value: 'mac'
},
{
field: 'browser',
value: 'chrome'
}
]
}
]
}
Index.vue
<template>
<div class="segments">
<segment
v-for="segment in segments"
:id="segment.id"
:name="segment.name"
:filters="segment.filters">
</segment>
</div>
</template>
<script>
import Segment from './Segment'
export default {
vuex: {
getters: {
segments
}
},
components: {
Segment
}
}
</script>
Segment.vue
<template>
<div class="segment">
<input type="text" class="name" v-model="name" />
<filters :filters="segment.filters"></filters>
<button #click="saveSegment()">Save</button>
</div>
</template>
<script>
import Filters from './Filters'
export default {
props: ['id', 'name', 'filters'],
vuex: {
methods: {
updateSegment({dispatch}, id, segments) {
dispatch(SEGMENT_UPDATE, id, segment)
}
}
},
methods: {
save () {
this.updateSegment(this.id, {
name: this.name,
filters: this.filters
})
}
}
}
</script>
Filters.vue
<template>
<ul class="filters">
<li v-for="filter in filter">
{{ filter.name }} <button #click="remove($index)">Remove</button>
</li>
</ul>
</template>
<script>
export default {
props: ['filters'],
methods: {
remove (index) {
this.filters.splice(index, 1)
}
}
}
</script>
Every time I remove a filter, I get Do not mutate vuex store state outside mutation handlers. error. And I know why, because no matter how many times I pass filters array through components properties, they still remain reactive and their change in Filters component propagates down to the vuex store that throws an error.
Same will be with segments names in inputs. But there is an example how to handle forms in docs.
But how can I make filters work? Make separate stores for filters? But it will be a mess because there will be multiple segments with their own filters on the page... I'm stuck :(

As well as I understood from the conversation in the vuex repo itself, the only way and the best decision is to deep clone the filters array and then just use it as a local variable.
It has to be done because arrays in JS always passed by reference and deep clone unbinds my filters array from the one in the vuex store.
This can be achieved by JSON.parse(JSON.stringify()), _.cloneDeep() or any other similarmethod.

Related

Vue - how can i update the default value of an input text field?

I'm loading a form component more times in the same page, that's because i have more forms for different tasks, so each form has different parameters.
Html page:
<div id="app">
<myForm formType="buy"></myForm>
<myForm formType="sell"></myForm>
<myForm formType="submit"></myForm>
<refreshAmount></refreshAmount>
</div>
And this is the form component:
<template>
<div>
<div v-if="formType=='buy'">
<form #submit.prevent="formSubmit()">
<input type="text" class="form-control" value="testetete" v-bind:value="amount">
<button v-if="side==='buy'" class="btn btn-primary" style="width: 100%">BUY</button>
<p>Available amount: {{$store.getters.amount}}</p>
</form>
</div>
...
</div>
</template>
<script>
export default {
props:{
formType:{
type:String,
default:'buy'
},
},
mounted() {
console.log('mounted')
},
data() {
return {
amount: this.$store.getters.amount
}
},
methods: {
...
}
}
</script>
Then i have the following store:
<script>
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
amount: 0
},
mutations: {
refreshAmount(state) {
fetch('SOME-URL')
.then(response => response.json())
.then(data => {
state.amount = 100
//state.amount = data['amount']
})
}
},
getters: {
amount: state => state.amount,
}
})
</script>
And finally, the refreshAmount component:
...
mounted() {
this.$store.commit('refreshBalance')
}
...
Basically, i need to show an amount in the form component. This amount is retrieved from my backend, and since i'm loading the form component 3 times, i would call my backend 3 times while i only need to call it once, so i decided to create the component refreshAmount that would call it once and pass it to the form components using a Vuex store.
The problem with my code is the following:
When i load the page, i'll see Available amount: 100 on all the 3 components, so that works; but in the default value of the text input form the value is 0. Why is that? Why isn't the value inside the input text field updated while <p>Available amount: {{$store.getters.amount}}</p> is updated?
Tl;dr: i'm using Vuex to set the value of a variable in my components, when i load the variable between a <p> tag the value is refreshed, while inside the input field the value of the variable stays the same.
amount is not updated because it's a data property, which only gets initialized when the component is first set up and not updated after. What you need is a computed property, which will keep track of the changes in the Vuex store. So instead of:
data() {
return {
amount: this.$store.getters.amount
}
}
you can do:
computed: {
amount() {
return this.$store.getters.amount
}
}

error Unexpected mutation of "todo" prop in vue.js (I'm using vue3)

I'm making a todo app in vue.js which has a component TodoItem
<template>
<div class="todo-item" v-bind:class="{'is-completed':todo.completed}">
<p>
<input type="checkbox" #change="markCompleted" />
{{todo.task}}
<button class="del">x</button>
</p>
</div>
</template>
<script>
export default {
name: "TodoItem",
props: ["todo"],
methods: {
markCompleted() {
this.todo.completed = true
},
},
};
</script>
todo prop that I'm passing:
{
id:1,
task:'todo 1',
completed:false
}
but it is throwing an error error Unexpected mutation of "todo" prop
Method 1 (Vue 2.3.0+) - From your parent component, you can pass prop with sync modifier
Parent Component
<TodoItem v-for="todo in todoList" :key="todo.id" todo_prop.sync="todo">
Child Component
<template>
<div class="todo-item" v-bind:class="{'is-completed':todo.completed}">
<p>
<input type="checkbox" #change="markCompleted" />
{{todo.task}}
<button class="del">x</button>
</p>
</div>
</template>
<script>
export default {
name: "TodoItem",
props: ["todo_prop"],
data() {
return {
todo: this.todo_prop
}
},
methods: {
markCompleted() {
this.todo.completed = true
},
},
};
</script>
Method 2 - Pass props from parent component without sync modifier and emit an event when the value changed. For this method, everything else is similar as well. Just need to emit an event when the todo item changed to completed.
The code is untested. Apologies if anything does not work.
What happen ? : Mutating a prop locally is now considered an anti-pattern, e.g. declaring a prop and then setting this.myProp = 'someOtherValue' in the component. Due to the new rendering mechanism, whenever the parent component re-renders, the child component’s local changes will be overwritten.
Solution : You can storage it as local data.
export default {
name: "TodoItem",
props: ["todo"],
data() {
return {
todoLocal: this.todo,
};
},
methods: {
markComplete() {
this.todoLocal.completed = !this.todoLocal.completed;
},
},
};
For me to fix this problem I store props in todos data im watching brad vue tutorials and i get this error this is my actual codes and its working.
<template>
<div class="todo-item" v-bind:class="{ 'is-complete': todo.completed }">
<p>
<input
type="checkbox"
v-on:change="markComplete(todo.completed)"
v-bind:checked="todo.completed"
/>
{{ todo.title }}
<!-- <button #click="$emit('del-todo', todo.id)" class="del">x</button> -->
</p>
</div>
</template>
<script>
export default {
name: 'TodoItem',
props: ['todo'],
data() {
return {
todos: this.todo,
}
},
methods: {
markComplete(isComplete) {
this.todos.completed = !isComplete
},
},
}
</script>
<style scoped>
.todo-item {
background: #f4f4f4;
padding: 10px;
border-bottom: 1px #ccc dotted;
}
.is-complete {
text-decoration: line-through;
}
.del {
background: #ff0000;
color: #fff;
border: none;
padding: 5px 9px;
border-radius: 50%;
cursor: pointer;
float: right;
}
</style>
One of the core principles of VueJS is that child components never mutate a prop.
All props form a one-way-down binding between the child property and the parent one: when the parent property updates, it will flow down to the child, but not the other way around.
If you wish to have the child component update todo.completed, you have two choices:
Use .sync modifier (Recommended)
This approach will require a bit of change to your props. You can read more about it here.
Parent component
<template>
<div>
...
<todo-item :task="nextTodo.task" :completed.sync="nextTodo.completed"/>
</div>
</template>
Child component
<template>
<div class="todo-item" v-bind:class="{'is-completed':completed}">
<p>
<input type="checkbox" #change="markCompleted" />
{{task}}
<button class="del">x</button>
</p>
</div>
</template>
<script>
export default {
name: "TodoItem",
props: ["task", "completed"],
methods: {
markCompleted() {
this.$emit('update:completed', true)
},
},
};
</script>
Use a custom event
Vue allows you set up listeners in your parent for events that the child will emit. Your child component can use this mechanism to ask the parent to change things. In fact, the above .sync modifier is doing exactly this behind the scenes.
Parent component
<template>
<div>
...
<todo-item :todo="nextTodo" #set-completed="$value => { nextTodo.completed = $value }/>
</div>
</template>
Child component
<template>
<div class="todo-item" v-bind:class="{'is-completed':todo.completed}">
<p>
<input type="checkbox" #change="markCompleted" />
{{todo.task}}
<button class="del">x</button>
</p>
</div>
</template>
<script>
export default {
name: "TodoItem",
props: ["todo"],
methods: {
markCompleted() {
this.$emit('set-completed', true)
},
},
};
</script>
You can't change a prop from inside a component - they are meant to be set by the parent only. It's a one-directional communication path.
You can try one of two things - either move your logic for detecting a todo has been completed to the parent, or feed the prop into a new variable in the data() lifecycle hook (this will only happen when the component is loaded for the first time, so you won't be able to update from outside the component, if that's important for your use case).
The canonical way to achieve n-deep prop binding in Vue 3 is to wrap your prop with a simple computed property. This is an example of a component that will communicate changes to it's selected property to it's parent--who is ultimately responsible for storing the state.
<template>
<!-- In this example my-child-component also has a "selected" prop -->
<my-child-component v-model:selected="syncSelectedId" />
</template>
<script lang="ts">
export default defineComponent({
components: { MyChildComponent },
props: {
selected: {
type: String,
required: true,
}
},
emits: ['update:selected'],
setup(props, context) {
const syncSelectedId = computed<string>({
get() {
return props.selected;
},
set(newVal: string) {
context.emit('update:selected', newVal);
},
});
return {
syncSelectedId,
}
}
});
So to re-iterate: With this strategy the highest level parent is the holder of the state. The code above assumes that there is a parent component in the hierarchy (so this component is just a "middle-man").
Then my-child-component can simply emit its own update:selected event to cause the state to change. That child will be updated appropriately through it's prop after the emit event causes the parent chain to propagate that change up (through emits) and then back down the component hierarchy (through props).
If you wanted to you could modify the code above to make it the "owner" of the state:
<template>
<my-child-component v-model:selected="selected" />
</template>
<script lang="ts">
export default defineComponent({
components: { MyChildComponent },
setup(props, context) {
const selected = ref('');
return {
selected,
}
}
});
And now of course you won't run into the "Unexpected mutation of X prop" error.
Another option is to have a prop that serves as a "default value" for a given state:
<template>
<my-child-component v-model:selected="selected" />
</template>
<script lang="ts">
export default defineComponent({
components: { MyChildComponent },
props: {
defaultSelected: {
type: String,
required: false,
default: ''
}
},
setup(props, context) {
const selected = ref(props.defaultSelected);
return {
selected,
}
}
});
And in this code above keep in mind that selected will NOT change if defaultSelected changes after the component has been initialized.
And lastly it's worth noting that you could write more sophisticated code to detect if a property is supplied--and if not use an internal state variable to store the value. I use this pattern for re-usable components that could be embedded in places where the parent wants to control the state OR in places where the parent is happy to delegate the storage of the state to the child:
<template>
<!-- In this example my-child-component also has a "selected" prop -->
<my-child-component v-model:selected="syncSelectedId" />
</template>
<script lang="ts">
export default defineComponent({
components: { MyChildComponent },
props: {
selected: {
type: String,
required: false,
default: null // Important: parent MUST pass non-null value if it wants to control state
}
},
emits: ['update:selected'],
setup(props, context) {
// This is state storage used if prop.selected is not provided
const _selected = ref('');
const syncSelectedId = computed<string>({
get() {
return props.selected === null ? _selected.value : props.selected;
},
set(newVal: string) {
if (props.selected !== null) {
// Using prop.selected as the driving model...
if (newVal !== props.selected) {
// We need to set to empty string (never null)
context.emit('update:selectedId', (newVal == null ? '' : newVal));
}
} else { // Storing selection state with _selectedId
if (newVal !== _selected.value) {
_selected.value = newVal == null ? '' : newVal;
context.emit('update:selected', _selected);
}
}
},
});
return {
syncSelectedId,
}
}
});
This last example is tricky... it gives special meaning to null and requires that you be very mindful of potential values of your state. In my example empty string is my representation for "no selection" and null is used as a flag for "no parent model of this state".
Mainly, property mutation is now deprecated and parent properties are overwritten when the parent component renders its DOM.
Here's the official documentation about it. We can still achieve this in multiple possible ways. Through a data property, a computed property, and component events.
When we want to pass this value back to the parent component as well as the nested child component of the current child component, using a data property would be useful as shown in the following example.
Example:
Calling your child component from the parent component like this.
Parent component:
<template>
<TodoItem :todoParent="todo" />
</template>
<script>
export default {
data() {
return {
todo: {
id:1,
task:'todo 1',
completed:false
}
};
}
}
</script>
Child component:
<template>
<div class="todo-item" v-bind:class="{'is-completed':todo.completed}">
<p>
<input type="checkbox" #change="markCompleted" />
{{todo.task}}
<button class="del">x</button>
</p>
</div>
</template>
<script>
export default {
name: "TodoItem",
props: ["todoParent"],
data() {
return {
todo: this.todoParent,
};
},
methods: {
markCompleted() {
this.todo.completed = true
},
},
};
</script>
Even you can pass this property to the nested child component and it won't give this error/warning.
Other use cases when you only need this property sync between parent and child component. It can be achieved using the sync modifier from Vue. v-model can also be useful. Many other examples are available in this question thread.
Example2: using component events.
We can emit the event from the child component as below.
Parent component:
<template>
<TodoItem :todo="todo" #markCompletedParent="markCompleted" />
</template>
<script>
export default {
data() {
return {
todo: {
id:1,
task:'todo 1',
completed:false
}
};
},
methods: {
markCompleted() {
this.todo.completed = true
},
}
}
</script>
Child component:
<template>
<div class="todo-item" v-bind:class="{'is-completed':todo.completed}">
<p>
<input type="checkbox" #change="markCompleted" />
{{todo.task}}
<button class="del">x</button>
</p>
</div>
</template>
<script>
export default {
name: "TodoItem",
props: ["todo"],
methods: {
markCompleted() {
this.$emit('markCompletedParent', true)
},
}
};
</script>
While you can still custom-bind events to handle this, .sync property extensions are considered deprecated. In Vue3 (at least) you can and usually should use the v-model:property declaration, similar to how you bind the property to the actual input. You just need to bind the inner input with :value and have it emit a matching update:property
<!-- CustomInput.vue -->
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="modelValue"
#input="$emit('update:modelValue', $event.target.value)"
/>
</template>
And use thusly:
<CustomInput v-model="searchText" />

Get input values from child components in Vue

I would like to retrieve all input values from my child components (client and advice, seen below), but not sure how to proceed.
client.vue
<template>
<div id="client">
<input type="text" v-model="client.name" />
<input type="text" v-model="client.code" />
</div>
</template>
<script>
export default {
data() {
return {
client: {
name: '',
code: '',
}
}
}
}
</script>
advice.vue
<template>
<div id="advice">
<input type="text" v-model="advice.foo" />
<input type="text" v-model="advice.bar" />
<div v-for="index in 2" :key="index">
<input type="text" v-model="advice.amount[index]" />
</div>
</div>
</template>
<script>
export default {
data() {
return {
advice: {
foo: '',
bar: '',
amount:[]
}
}
}
}
</script>
Each component has more fields than the above example.
My home page (parent) looks as simple as:
<template>
<form id="app" #submit="printForm">
<clientInfo />
<advice />
<input type="submit" value="Print" class="btn" />
</form>
</template>
<script>
import clientInfo from "#/components/clientInfo.vue";
import advice from "#/components/advice.vue";
export default {
components: {
clientInfo,
advice
},
methods: {
printForm() {}
}
}
</script>
My first idea was to $emit, but not sure how to do that efficiently with more than 20 fields without attaching a #emitMethod="parentEmitMethod" to every single field.
My second idea was to have a Vuex store (as seen below), but I don't know how to save all the states at once and not sure if I should.
new Vuex.Store({
state: {
client: {
name:'',
code:''
},
advice: {
foo:'',
bar:'',
amount:[]
}
}
})
You could use FormData to get the values of the form's <input>s or <textarea>s that have a name attribute (anonymous ones are ignored). This works even if the form has nested Vue components that contain <input>s.
export default {
methods: {
printForm(e) {
const form = e.target
const formData = new FormData(form) // get all named inputs in form
for (const [inputName, value] of formData) {
console.log({ inputName, value })
}
}
}
}
demo
You could use v-model with your custom components. Let's say you want to use them like this:
<advice v-model="adviceData"/>
For this, you would need to watch for value changes on your input elements inside your advice component and then emit an input event with the values. This will update the adviceData binded property. One generic way to do this could be including a watcher inside your advice component, like this:
export default {
data() {
return {
advice: {
foo: '',
bar: '',
amount:[]
}
}
},
watch: {
advice: {
handler: function(newValue, oldValue) {
this.$emit('input', newValue);
},
deep: true,
}
},
}
This way you will not have to add a handler for each input field. The deep option must be included if we need to detect changes on nested data in the advice object.
I think you can achieve what you want is when the user writes something using#change this will trigger a method when the input value is changed, you could use a button instead or anything you want, like this:
The child component
<input type="text" v-model="advice.amount[index]" #change="emitCurrStateToParent ()"/>
You gotta add #change="emitCurrStateToParent ()" in every input you have.
emitCurrStateToParent () {
this.$emit("emitCurrStateToParent", this.advice)
}
Then in you parent component
<child-component v-on:emitCurrStateToParent="reciveDataFromChild ($event)"></child-component>
reciveDataFromChild (recivedData) {
// Do something with the data
}
I would use a button instead of #change, like a "Save" button idk, the same goes for vuex, you can use the #change event
saveDataAdviceInStore () {
this.$store.commit("saveAdvice", this.advice)
}
Then in the store
mutations: {
saveAdvice (state, advice) {
state.advice = advice
}
}

Prop passed to child component is undefined in created method

I am using Vue.js 2.
I have a problem with passing value to the child component as a prop. I am trying to pass card to card-component.
In card-component I can access the prop in the Card goes here {{card}} section.
However when I try to access it in created or mounted methods it's undefined.
Parent:
<template>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<card-component :card="place.card"></card-component>
</div>
</div>
</div>
</template>
<script>
import CostComponent from './CostComponent';
import CardComponent from './CardComponent';
export default {
components: {
CostComponent, CardComponent
},
props: ['id'],
data() {
return {
place: []
}
},
created() {
axios.get('/api/places/' + this.id)
.then(response => this.place = response.data);
}
}
</script>
Child:
<template>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<ul class="list-unstyled">
Card goes here {{card}}
</ul>
</div>
</div>
</div>
</template>
<script>
import CardItemComponent from './CardItemComponent';
export default {
components: {
CardItemComponent
},
props: ['card'],
created() {
console.log(this.card); // undefined
},
mounted() {
console.log(this.card); // undefined
},
}
</script>
I did a lot of googling but none of the solutions I found have fixed my issue.
This is purely a timing issue. Here's what happens...
Your parent component is created. At this time it has an empty array assigned to place (this is also a problem but I'll get to that later). An async request is started
Your parent component creates a CardComponent instance via its template
<card-component :card="place.card"></card-component>
at this stage, place is still an empty array, therefore place.card is undefined
3. The CardComponent created hook runs, logging undefined
4. The CardComponent is mounted and its mounted hook runs (same logging result as created)
5. Your parent component is mounted
6. At some point after this, the async request resolves and changes place from an empty array to an object, presumably with a card property.
7. The new card property is passed down into your CardComponent and it reactively updates the displayed {{ card }} value in its template.
If you want to catch when the card prop data changes, you can use the beforeUpdate hook
beforeUpdate () {
console.log(this.card)
}
Demo
Vue.component('CardComponent', {
template: '<pre>card = {{ card }}</pre>',
props: ['card'],
created () {
console.log('created:', this.card)
},
mounted () {
console.log('mounted:', this.card)
},
beforeUpdate () {
console.log('beforeUpdate:', this.card)
}
})
new Vue({
el: '#app',
data: {
place: {}
},
created () {
setTimeout(() => {
this.place = { card: 'Ace of Spades' }
}, 2000)
}
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<card-component :card="place.card" />
</div>
See https://v2.vuejs.org/v2/guide/instance.html#Lifecycle-Diagram
If place is meant to be an object, you should not be initialising it as an array. Also, if your CardComponent relies on data being present, you may want to conditionally render it.
For example
data () {
return { place: null }
}
and
<card-component v-if="place" :card="place.card"></card-component>
then CardComponent will only be created and mounted after place has data.
Make sure you have props: true in the router file. It is a simple solution but many of us forget this.
{
path: '/path-to',
name: 'Name To',
component: Component,
props: true
}

Vuex - changing value based on component's current index and passing the value to other components

I have hit a wall and cannot get over it without your help now. I've spent a good few days trying to get my head around mutations and actions but this particular case I have doesn't seem to apply to tutorials online, also the answers on here are for different scenarios to mine. So here goes:
Setup:
- Project is using vuex and store for data and state management
- Currently App.vue has two child components: PizzaGenerator.vue and BaseButtons.vue
I am trying to achieve this:
- When I click on a specific button in BaseButtons.vue I need a centrally managed showBaseIndex to be assigned an index value. This value is then available to the other, PizzaGenerator.vue, component which will reflect the change and show a layer that matches the new value of showBaseIndex.
Please see all the two components and store below.
Can you help me head in the right direction?
PizzaGenerator.vue
<template>
<div class="pizza-generator section" id="screen3" data-anchor="screenThree">
<ul class="pizza-layers">
<!-- Display pizzas -->
<li v-for="(item, index) in getBase" class="pizza-canvas pizza-canvas--base" v-bind:class="item.class" v-if="$store.state.showBaseIndex == index"></li>
<!-- END Display pizzas -->
</ul>
<div class="pizza-items">
<app-base-buttons></app-base-buttons>
</div>
</div>
</template>
<script>
import Base from './pizza-buttons/BaseButtons'
import { mapGetters, mapActions } from 'vuex'
export default {
components: {
appBaseButtons: Base
},
computed: {
getBase(){
return this.$store.state.base
}
},
methods: mapActions([
'baseLayer',
]),
}
</script>
BaseButtons.vue
<div class="pizza-items-selector pizza-items-selector--dough">
<div class="pizza-items-selector__items pizza-items-selector__items--dough">
<div class="sliding-buttons">
<button v-for="(item, index) in getBase" class="sliding-buttons__button dough-button" :key="index" #click="baseLayer = index"> {{ item.name }}</button>
</div>
<button class="buttons-prev-1 prev">prev</button>
<button class="buttons-next-1 next">next</button>
</div>
</div>
<script>
import { mapActions, mapMutations } from 'vuex'
export default {
computed:{
getBase(){
return this.$store.state.base
},
},
methods:{
...mapMutations([
'baseLayer',
]),
baseLayerIndex() {
this.$store.commit('baseLayer');
}
},
}
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
showBaseIndex: 1,
base : [
{ name: 'Dünner Italienisch', class: 'pizza-canvas--base-italienisch', id: 1 },
{ name: 'Dünner Italienisch - Vollkorn', class: 'pizza-canvas--base-italienisch--vollkorn', id: 2 },
{ name: 'Dicker Amerikanisch', class: 'pizza-canvas--base-amerikanisch', id: 3 },
{ name: 'Dicker Amerikanisch / Chili-Käse-Rand', class: 'pizza-canvas--base-amerikanisch--chilli-kaese-rand', id: 4 },
{ name: 'Dicker Amerikanisch / Käse-Rand', class: 'pizza-canvas--base-amerikanisch--kaese-rand', id: 5 }
],
},
getters: {
//
},
mutations: {
baseLayer (state){
state.showBaseIndex
}
},
});
export default store;
Mutations are functions, not simple values. You should check the Vuex guide about mutations, they are quite straightforward.
What you should do is declaring the given mutation in this way, so it will also accept a parameter:
mutations: {
baseLayer (state, id){
state.showBaseIndex = id;
}
},
and commit the mutation properly in the component:
methods:{
...mapMutations([
'baseLayer',
]),
baseLayerIndex(index) { // call this at #click on button, giving the index as parameter
this.$store.commit('baseLayer', index);
}
}
This will set the desired index in the store, and from that on you could get the current base from store using vuex getters like:
getters: {
getSelectedBase(state){
return state.base.find(base=>{return base.id === state.showBaseIndex});
}
},