Vuex 更新后强制 props 在子组件中更新

Posted

技术标签:

【中文标题】Vuex 更新后强制 props 在子组件中更新【英文标题】:Force props to update in child component after Vuex update 【发布时间】:2020-12-10 10:45:40 【问题描述】:

我有一个子组件 (<BaseProjectTable>),它在我的整个站点中重复使用,其中包含一个 Vuetify <v-data-table> 组件。数据表的标题和内容项作为 props 传递到组件中,项目数据作为 mapGetter 从 Vuex 存储映射到父级。子组件包含每一行的编辑功能,我使用mapAction 从那里更新 API 和 Vuex 数据,其想法是,由于我的 mapGetter 是反应式的,它应该更新数据并因此更新数据表展示。但是,这不起作用;我可以通过开发工具看到状态更新得很好,但是子组件显示没有。

这是子<BaseProjectTable>组件的相关部分:

<template>
  <div>
    <v-data-table
      show-expand
      :headers="headers"
      :items="filteredItems"
      :search="search"
      tem-key="sow"
      @click:row="rowClick"
      :hide-default-footer="disablePagination"
      dense
      :disable-pagination="disablePagination"
    >
    ...
    </v-data-table>

    ...
    export default 
    name: "BaseProjectTable",
    props: 
      headers: Array,
      items: Array,
      loggedInUser: Object,
      title: String,
      max2chars: v => v.length <= 2 || 'Input too long!',
      editDialog: false,
      showPracticeFilter: 
        default: true,
        type: Boolean
      ,
      showSEFilter: 
        default: true,
        type: Boolean
      ,
      showStatusFilter: 
        default: true,
        type: Boolean
      ,
      projectType: 
        type: String,
        default: 'active'
      ,
      disablePagination: 
        type: Boolean,
        default: true
      ,
    ,
  ,
  methods: 
  ...mapActions('projects', 
        saveProject: 'saveProject',
    ), 
    save() 
      // Update API and store with new data.
      this.saveProject(
        projectType: this.projectType,
        projectData: this.editedItem
      )
  ,
  computed: 
      statuses()  
        return this.projectType === 'active' ? this.activeStatuses : this.oppStatuses;
      ,
      filteredItems() 
        return this.items.filter(d => 
          return Object.keys(this.filters).every(f => 
            return this.filters[f].length < 1 || this.filters[f].includes(d[f])
          )
        )
      ,
    

这里是父组件的相关代码:

<v-card>
  <BaseProjectTable
    :headers="headers"
    :items="activeProjects"
    :loggedInUser="loggedInUser"
    title="Active Projects"
    :disablePagination=false
  ></BaseProjectTable>
</v-card>
...
export default 
  computed: 
    ...mapGetters('projects', 
      activeProjects: 'activeProjects',
      closedProjects: 'closedProjects',
      opportunities: 'opportunities'
    ),
  

save() 方法更新了我的 Vuex 存储中由 activeProjects map getter 引用的数据(我已经在 Vue devtools 中验证了这是真的)。它还显示了组件本身中的 items 值,该值在开发工具的 Components 选项卡中更新。由于使用mapGetters 会使数据具有反应性,因此我预计它也会更新我的子组件中的数据,但事实并非如此。

根据我看到的here,我尝试了 v-data-table> 的item-key 属性,如下所示:

<v-data-table
  show-expand
  :headers="headers"
  :items="filteredItems"
  :search="search"
  item-key="sow"
  @click:row="rowClick"
  :hide-default-footer="disablePagination"
  dense
  :disable-pagination="disablePagination"
>

(使用此组件的每条记录都将具有唯一的sow 键),但这不起作用。

我能想到的唯一想法是我如何编辑突变中的数据:

export const state = () => (
    active: [],
    closed: [],
    opportunities: [],
)

export const getters = 
  activeProjects: state => state.active,
  closedProjects: state => state.closed,
  opportunities: state => state.opportunities,


export const actions = 
  saveProject( commit , projectType, projectData) 
    commit(types.SAVE_PROJECT, projectType, projectData);
  


export const mutations = 
  [types.SAVE_PROJECT](state, projectType, projectData) 
    // Get project from state list by sow field.
    const index = state[projectType].findIndex(p => p.sow === projectData.sow);
    state[projectType][index] = projectData;
  

与替换整个 state[projectType] 值相比。

我需要做什么才能让数据表显示我更新的值?

【问题讨论】:

active in store 和 activeProjects 一样吗?无法关联突变 - 我可能在那里错过了一些东西。您是否尝试用this.$set(state[projectType], index, projectData) 替换state[projectType][index] = projectData; - 这应该确保反应性并反映getter 的变化。 是的(我在上面的代码中添加了吸气剂)。我的问题是我可以在 Vue devtools 中看到我在 getter 中的更改 - 它们只是没有反映在显示中。 【参考方案1】:

来自 Vue documentation,

Vue 无法检测到数组的以下更改

当您直接使用索引设置项目时,例如vm.items[indexOfItem] = newValue 当您修改数组的长度时,例如vm.items.length = newLength

用你所拥有的替换

import Vue from 'vue'; // this should be at the top level

export const mutations = 
  [types.SAVE_PROJECT](state, projectType, projectData) 
    // Get project from state list by sow field.
    const index = state[projectType].findIndex(p => p.sow === projectData.sow);
    Vue.set(state[projectType], index, projectData)
  

在此之后,将检测到数组的更改,并且 getter 将按预期工作。

【讨论】:

嗯,在突变中使用 Vue.set() 似乎很奇怪,但它确实有效。 Vuex documentation 确认在突变中使用 Vue.set 是合适的。 如果您不介意,请接受答案,以帮助任何偶然发现此页面的人@wonder95

以上是关于Vuex 更新后强制 props 在子组件中更新的主要内容,如果未能解决你的问题,请参考以下文章

Vue子组件中 data 从props中动态更新数据

Vue 道具没有在子组件中正确更新

Vue.js 父子组件,为啥我的 prop 在子组件中还没有?

React生命周期, setState、props改变触发的钩子函数

vue props原理

VueJS组件通讯