使用Vuex通过表单更新对象数据无效果,求排查与解决方法
Problem Statement
I'm trying to update a category object via a form using Vuex. When I click the Update button, the console logs the modified category data correctly, but the categories array in my Vuex state doesn't update at all. Here's my code:
Store Code
const state = { categories: [] }; //mutations: [mutationType.UPDATE_CATEGORY] (state, id, category) { const record = state.categories.find(element => element.id === id); state.categories[record] = category; } //actions: updateCategory({commit}, id, category) { categoriesApi.updateCategory(id, category).then((response) => { commit(mutationType.UPDATE_CATEGORY, id, response); router.push({name: 'categories'}); }) }
Vue Component Template
<form> <div class="form-group"> <label for="Name">Name</label> <input type="text" class="form-control form-control-sm" name="name" v-model.lazy="category.name" required> </div> <div class="form-group"> <label for="Slug">Slug</label> <input type="text" class="form-control form-control-sm" name="slug" v-model.lazy="category.slug" required> </div> <div class="form-group"> <label for="Avatar">Avatar</label> <input type="text" class="form-control form-control-sm" name="avatar" v-model.lazy="category.avatar" required> </div> <div class="form-group"> <label for="Description">Description</label> <textarea type="text" class="form-control form-control-sm" rows="5" name="description" v-model.lazy="category.description"></textarea> </div> <div class="form-group"> <button type="submit" @click.prevent="updateCategory" class="btn btn-outline btn-sm">Update</button> </div> </form>
Vue Component Script
export default { data() { return { category: {} } }, methods: { getCategoryById(id) { axios.get(`categories/${id}`) .then(response => { this.category = response.data; }) .catch(error => { console.log(error); }) }, // Using mutation. updateCategory() { this.$store.dispatch('updateCategory', this.$route.params.id, this.category); console.log(this.category); //Display exactly data changed. } }, created() { this.getCategoryById(this.$route.params.id); } }
Root Causes
There are two critical issues preventing your state from updating:
- Incorrect Parameter Passing in Vuex Actions/Mutations
Vuex actions and mutations only accept a maximum of two parameters:
- For mutations: first is
state, second is a singlepayload(you can't pass multiple separate arguments) - For actions: first is the
contextobject, second is a singlepayload
Right now, you're dispatching with three arguments ('updateCategory', id, category), but the action will only receive id as the payload—category gets dropped entirely. The same happens when you commit the mutation: the third argument (response) is ignored, so your mutation never gets the updated category data.
- Broken Array Update Logic in Mutation
You correctly find the matchingrecordusingfind(), but then you try to update the array withstate.categories[record] = category. This doesn't work becauserecordis the object itself, not its index in the array. Vue can't locate the position to update, so the state remains unchanged.
Additionally, we need to make sure the update triggers Vue's reactivity system properly.
Step-by-Step Fixes
1. Refactor Action/Mutation to Use a Payload Object
Pack all your data into a single payload object to pass between components, actions, and mutations:
Updated Store Action
updateCategory({commit}, payload) { categoriesApi.updateCategory(payload.id, payload.category).then((response) => { // Pass id and the updated category as a payload object commit(mutationType.UPDATE_CATEGORY, { id: payload.id, updatedCategory: response }); router.push({name: 'categories'}); }) }
Updated Store Mutation
[mutationType.UPDATE_CATEGORY] (state, payload) { // Find the INDEX of the matching category const categoryIndex = state.categories.findIndex(element => element.id === payload.id); if (categoryIndex !== -1) { // Option 1: Replace the entire element (simple, works for reactivity) state.categories.splice(categoryIndex, 1, payload.updatedCategory); // Option 2: Merge new data into the existing object (preserves reactivity for nested properties) // Object.assign(state.categories[categoryIndex], payload.updatedCategory); } }
2. Fix the Dispatch Call in Your Component
Update the updateCategory method to send a payload object instead of separate arguments:
updateCategory() { this.$store.dispatch('updateCategory', { id: this.$route.params.id, category: this.category }); }
3. (Optional) Ensure Reactivity for the category Object
If your API returns a category with properties that aren't initially present in the empty category object in data(), you might need to reassign it to ensure reactivity (though your current v-model setup should handle this fine). If you run into issues later, you could use:
// In getCategoryById's then() block this.category = {...response.data}; // Spread to create a new reactive object
Verification
After making these changes, clicking the Update button will:
- Send the correct id and category data to your Vuex action
- Pass the updated category from the API to the mutation via a payload
- Correctly locate and update the category in the
categoriesarray - Trigger Vue's reactivity system to update any components using this state
内容的提问来源于stack exchange,提问作者Thuan Nguyen

