Vue3/Vuex - Where am I "mutating vuex store state outside mutation handlers"?

Viewed 826

When calling a Vuex Action where Firebase successfully updates, followed by a Vuex Mutation on state.profile, Vue triggers the following errors upon save within watch().

[Vue warn]: Unhandled error during execution of watcher callback

Error: [vuex] do not mutate vuex store state outside mutation handlers.

I have cloned and then merged store.state.profile into formData via rfdc.

Form values are updated within it's own reactive() data object formData, versus immediately committing to store in order to later provide both save() and cancel() options.

template

<template>
    <element-textarea
        id="about"
        title="About"
        :rows="10"
        v-model="about"
    ></element-textarea>
    <element-checkbox
        v-for="(specialty, i) in jSpecialties"
        :key="i"
        :id="specialty.toLowerCase()"
        :title="specialty"
        v-model="specialties[specialty]"
    >
        {{ specialty }}
    </element-checkbox>
</template>

script

...
const clone = require('rfdc/default')

export default defineComponent({
    ...
    setup(){
        ...
        let specialties: { [key: string]: boolean } = {}
        jSpecialties.map(name => (specialties[name] = false))
        const formData = reactive({
            about: '',
            specialties
        })

        watch(
            () => store.state.profile,
            () => {
                try {
                    Object.assign(formData, clone(store.state.profile))
                } catch (err) {
                    console.log('Error Appears Here', err)
                }
            }
        )

        const save = () =>
            store.dispatch('updateProfile', formData)
                .catch(err => console.log(err))

        return { ...toRefs(formData), save }
    }
})

vuex

export default createStore({
    strict: true,
    state: {
        profile: {}
    },
    mutations: {
        setProfile: (state, payload) => state.profile = payload,
    },
    actions: {
        updateProfile: ({state, commit}, data) => {
            const update = () =>
                db
                    .collection(...)
                    .doc(...)
                    .set(data, {merge: true})
                    .then(_ => commit('setProfile', data))
                    .catch(err => console.log(err))
            return update()
        }
    }
})

When removing strict: true from vuex, I receive this error:

Maximum recursive updates exceeded. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.


Replacing watch() with watchEffect() get rid of the Maximum recursive updates exceeded error.

watchEffect(() => {
    try {
        Object.assign(formData, clone(store.state.profile))
    } catch (error) {
        console.log(error)
    }
})

However, when updating Vuex with strict: true, I am still receiving the following:

Error: [vuex] do not mutate vuex store state outside mutation handlers.

1 Answers

I've discovered the issue. I overlooked my copying of a reference object back to state.

I needed to clone formData prior to dispatching, as the same data object was also committed to store via actions.

const save = () =>
    store.dispatch('updateProfile', clone(formData))
        .catch(err => console.log(err))
Related