Search code examples
vuexstoregetterreactivevuedraggable

getters not reactive in vuex


I have following store defined:

 state: () => ({
    infoPackCreationData: null,
    infoPackCreationTab: null,
  }),
getters: {
  infoPackImage(state: any) {
      return state.infoPackCreationTab && state.infoPackCreationTab.infopackContents
        ? state.infoPackCreationTab.infopackContents.filter((item: any) => item.type === "IMAGE")
        : [];
    }
},
mutations: {
  setImageData(state:any, infopackImageData: any) {
      state.infoPackCreationTab.infopackContents.filter((item: any) => {if(item.type === "IMAGE")
      item = infopackImageData
        console.log(item , 'this is items');
      return item})
    }
},
 actions: {
    setImageData(context: any, payload: any) {
      context.commit('setImageData', payload)
    }
}

and in my component I am using the computed to get the imageList:

  computed: {
      ...mapGetters("creationStore", ["infoPackImage"]),
      imageList: {
        get() {
          return this.infoPackImage ?? [];
        },
        set(value) {
         this.$store.dispatch('creationStore/setImageData', value);
        }
      }
    },

The problem is I want to edit a value of the imageList by index using draggable libarary, but imageList does not act reactive and it just move the image and not showing the other image in the previous index:

  async imageChange(e) {
        this.loading = true
        let newIndex = e.moved.newIndex;
        let prevOrder = this.imageList[newIndex - 1]?.order ?? 0
        let nextOrder = this.imageList[newIndex + 1]?.order ?? 0
        const changeImageOrder = new InfopackImageService();
        try {
          return await changeImageOrder.putImageApi(this.$route.params.infopackId,
            this.$route.params.tabId,
            e.moved.element.id, {
              title: e.moved.element.title,
              infopackAssetRef: e.moved.element.infopackAssetRef,
              order: nextOrder,
              previousOrder: prevOrder,
            }).then((res) => {
            let image = {}
            let infopackAsset = e.moved.element.infopackAsset
            image = {...res, infopackAsset};
            Vue.set(this.imageList, newIndex , image)
            this.loading = false
            return this.imageList
          });
        } catch (e) {
          console.log(e, 'this is put error for tab change')
        }
      },

Solution

  • Array.prototype.filter doesn't modify an array in-place, it returns a new array. So this mutation isn't ever changing any state:

    mutations: {
      setImageData(state:any, infopackImageData: any) {
          state.infoPackCreationTab.infopackContents.filter((item: any) => {if(item.type === "IMAGE")
          item = infopackImageData
            console.log(item , 'this is items');
          return item})
        }
    },
    

    So, if you intend to change state.infoPackCreationTab.infopackContents, you'll need to assign the result of filter():

    mutations: {
      setImageData(state:any, infopackImageData: any) {
          state.infoPackCreationTab.infopackContents = state.infoPackCreationTab.infopackContents.filter(...)
    

    However, since state.infoPackCreationTab did not have an infopackContents property during initialization, it will not be reactive unless you use Vue.set() or just replace the whole infoPackCreationTab object with a new one (see: Vuex on reactive mutations):

    mutations: {
      setImageData(state:any, infopackImageData: any) {
        state.infoPackCreationTab = {
          ...state.infoPackCreationTab,
          infopackContents: state.infoPackCreationTab.infopackContents.filter(...)
        };