Vuex:无法更改动作内深度嵌套的状态数据

Nad*_*bas 8 javascript vue.js vuex

在商店中,我有一个更新一些数据的操作,该操作如下所示:


setRoomImage({ state }, { room, index, subIndex, image }) {
      state.fullReport.rooms[room].items[index].items[subIndex].image = image;
      console.log(state.fullReport.rooms[room].items[index].items[subIndex])
    },
Run Code Online (Sandbox Code Playgroud)

因为所有这些数据都是动态的,所以我必须动态更改嵌套值并且不能直接对属性进行硬编码。数据如下所示:

fullreport: {
    rooms: {
        abc: {
          items: [
            {
              type: "image-only",
              items: [
                {
                  label: "Main Image 1",
                  image: ""
                },
                {
                  label: "Main Image 2",
                  image: ""
                }
              ]
            }
          ]
        }
      }
}
Run Code Online (Sandbox Code Playgroud)

当我分派动作时,在控制台中我可以看到子属性的值image已成功改变,但是如果我从 Chrome 中的 Vue DevTools 访问 VueX 存储,我看到该值在那里没有改变。这是控制台输出:

在此处输入图片说明

拜托,有人能告诉为什么会这样吗?据我所知,数据正在成功更改,但不知何故状态没有显示它,因此我的组件不会重新渲染。

我也尝试使用Vue.set而不是简单的赋值,但仍然没有运气:(

Vue.set(
  state.fullReport.rooms[room].items[index].items[subIndex],
  "image",
   image
 );
Run Code Online (Sandbox Code Playgroud)

编辑:

按照David Gard 的回答,我尝试了以下操作:

我也在使用 Lodash _(我知道制作对象的整个副本不好),这是变异代码块。

let fullReportCopy = _.cloneDeep(state.fullReport);
fullReportCopy.rooms[room].items[index].items[subIndex].image = image;
Vue.set(state, "fullReport", fullReportCopy);
Run Code Online (Sandbox Code Playgroud)

现在在计算属性中,它state.fullReport是一个依赖项,我有一个console.log只要重新计算计算属性就会打印出一个字符串。

每次我提交这个突变时,我都会看到计算属性记录字符串,但它接收的状态仍然没有改变,我猜Vue.set只是告诉计算属性状态改变了,但它实际上并没有改变它。因此,我的组件的 UI 没有变化。

Mar*_*cRo 6

正如评论中提到的 - 如果您在商店中保留深度嵌套状态,它很快就会变得复杂。

问题是,您必须以两种不同的方式填充数组和对象,因此,请考虑是否需要访问它们的本机方法。不幸的是,Vuex 还不支持反应式地图。

除此之外,我还处理需要动态设置多个嵌套级别的属性的项目。一种方法是递归设置每个属性。

它并不漂亮,但它有效:

function createReactiveNestedObject(rootProp, object) {
// root is your rootProperty; e.g. state.fullReport
// object is the entire nested object you want to set

  let root = rootProp;
  const isArray = root instanceof Array;
  // you need to fill Arrays with native Array methods (.push())
  // and Object with Vue.set()

  Object.keys(object).forEach((key, i) => {
    if (object[key] instanceof Array) {
      createReactiveArray(isArray, root, key, object[key])
    } else if (object[key] instanceof Object) {
      createReactiveObject(isArray, root, key, object[key]);
    } else {
      setReactiveValue(isArray, root, key, object[key])
    }
  })
}

function createReactiveArray(isArray, root, key, values) {
  if (isArray) {
    root.push([]);
  } else {
    Vue.set(root, key, []);
  }
  fillArray(root[key], values)
}

function fillArray(rootArray, arrayElements) {
  arrayElements.forEach((element, i) => {
    if (element instanceof Array) {
      rootArray.push([])
    } else if (element instanceof Object) {
      rootArray.push({});
    } else {
      rootArray.push(element);
    }
    createReactiveNestedFilterObject(rootArray[i], element);
  })
}

function createReactiveObject(isArray, obj, key, values) {
  if (isArray) {
    obj.push({});
  } else {
    Vue.set(obj, key, {});
  }
  createReactiveNestedFilterObject(obj[key], values);
}

function setValue(isArray, obj, key, value) {
  if (isArray) {
    obj.push(value);
  } else {
    Vue.set(obj, key, value);
  }
}
Run Code Online (Sandbox Code Playgroud)

如果有人有更聪明的方法来做到这一点,我非常乐意听到!

编辑:

我使用上面发布的解决方案的方式是这样的:

// in store/actions.js

export const actions = {
  ...
  async prepareReactiveObject({ commit }, rawObject) {
    commit('CREATE_REACTIVE_OBJECT', rawObject);
  },
  ...
}
Run Code Online (Sandbox Code Playgroud)
// in store/mutations.js
import { helper } from './helpers';

export const mutations = {
  ...
  CREATE_REACTIVE_OBJECT(state, rawObject) {
    helper.createReactiveNestedObject(state.rootProperty, rawObject);
  },
  ...
}
Run Code Online (Sandbox Code Playgroud)
// in store/helper.js

// the above functions and

export const helper = {
  createReactiveNestedObject
}
Run Code Online (Sandbox Code Playgroud)


Dav*_* SK 0

排除评论中的良好做法。

您需要的是:在对象发生变化时指示 Vue(复杂对象不是反应性的)。使用 Vue.set。您需要设置整个对象:

   Vue.set(
     state,
     "fullReport",
     state.fullReport
   );
Run Code Online (Sandbox Code Playgroud)

文档: https: //v2.vuejs.org/v2/api/#Vue-set

  • Vue.set() 向对象添加响应式属性。您不能用它设置嵌套对象。如果要设置嵌套对象,则需要递归设置每个属性。 (3认同)