首页 > 解决方案 > 更改组件时更新 Vuex 存储中的数组中的项目

问题描述

在我的 Vuexstore中有一组注释。每个音符都由一个 表示<textarea>。我有一个显示每个注释的 NoteArray 组件:

// NoteArray.vue
export default {
  name: "NoteArray",
  components: { Note },
  computed: {
    ...mapState({
      notes: state => state.notes // get array of Notes from store
    })
  },
  template: `
    <div v-for="note in notes">
      <!-- make one Note per note in array -->
      <Note :contents.sync="note.contents"></Note>
    </div>`
}
// Note.vue
export default {
  name: "Note",
  props: ["contents"], // recieve contents from NoteArray
  template: `<textarea v-model="contents"></textarea>`
}

如果我不使用 Vuex,此设置可能会正常工作,但我希望每个 Note 的内容由我的商店中的单个数组表示:

// index.ts
let store = new Vuex.Store({
  state: {
    notes: [{contents: ""}] // will have a mutation to add/remove notes
  }
}

现在,我正在使用v-model将每个 Note 的内容附加到自身。这适用于单向绑定——Notes 的初始状态很好地向下传播。尝试更改注释时会出现问题。此处将使用sync修饰符来建立双向绑定,而无需我定义任何输入事件。

Vuex 不是这样——我只能使用突变来修改状态。启用严格模式后,上面的示例会导致错误[vuex] do not mutate vuex store state outside mutation handlers

此处的解决方法是定义一个由给定 Note 调用的突变,该突变会@input更改该 Note 的值contents。我能想到的唯一方法是定义一个访问内容并更改它的突变(而不是v-modeland sync):

// index.ts
...
  mutations: {
    update_note(state, payload) {
      state.notes[payload.index] = payload.context
    }
  }
...

...但这要求每个 Note 知道并且能够将其在state.notes数组中的索引传递给突变。但是,每个 Note 都完全不知道其上下文 - 他们没有此信息。

我不确定从这里去哪里 - 我怎样才能让每个 Note 的值在用户更改它们时contents更新?store我希望 NoteArray 和 Note 保持它们自己的组件。

上述示例的实现:https ://codesandbox.io/s/keen-moser-oe63d

标签: vue.jsvuex

解决方案


经过一番挖掘,事实证明,您所要做的就是放弃v-model$emit('update:contents', $event.target.value)在. 实际上并不需要我最初的答案所包含的所有其他内容。@input<textarea>

这是一个工作示例

如您所见,注释已更新,没有任何内容commit,并且App.vue正确显示。我将测试放入App.vue以确保它们在状态中更新,而不仅仅是vmNoteList.vue.

我添加了唯一标识符,因为我发现,如果没有它们,删除 note 时<textarea>会显示错误的内容(来自 notes 数组中的下一个注释)。
这正是要避免按索引键控的原因。(阅读本文档部分末尾的警告)。


现在,完全公平地说,我真的不明白为什么修改通过.sync不会触发“不要在商店外变异”警告。要回答这个问题,必须深入研究究竟做了.sync什么。或者它可能与不改变对象的结构有关。不太确定。

无论如何,这样做的正确方法是调度一个动作,update:contents该动作将提交一个更新存储的突变:

示例:https ://codesandbox.io/s/frosty-feather-lhurk?file=/src/components/NoteList.vue 。


另一个注意事项:正如这个讨论所示,在状态属性之前prop.sync没有使用“神奇地工作”开箱即用,所以它确实需要显然不再需要的dispatch+ 。commit


推荐阅读