Search code examples
vue.jsvuejs3vue-componentstore

Watcher does not detect changes occured to an array of objects


I am using vue3 with options api as shown in the section titled code. I set an object to a store. storeTest.vue shows how the Store is implemented, and in watch it shows how I listen to changes that occurred in the store.

At run time, despite there being values assigned to either of geojson and order, the watch statement does not report any changes occurring. I expect that the values assigned to geojson and order are to be printed in the console, but nothing gets displayed.

Why that is happening and how to solve it?

code:

StoreTest.setters.set({
    geojson: this.#featureRecentlyRequestedProcessing.value.get(DigitizePolygonConstants.CONST_DIGITIZED_FEATURE_PROPERTY_GEOJSON.description),
    order: this.#featureRecentlyRequestedProcessing.value.get(DigitizePolygonConstants.CONST_DIGITIZED_FEATURE_PROPERTY_GEOM_ORDER.description),
});

storeTest.vue:

<script>
import { reactive } from 'vue';

export default {
  }

export const StoreTest = ({
  state: reactive({
    array: [],
  }),
  getters: {
    get() {
      return StoreTest.state.array;
    },
  },
  setters: {
    set(obj) {
        StoreTest.state.array.push(obj);
    },
  },
  actions: {
    initialize() {
        StoreTest.state.array = [];
    },
  }
});
</script>

watch:

created() {
    console.log(verboseTag, 'created()');

    this.$watch(this.refStoreTest.getters.get, (newVal, oldVal) => {
        msg = JSON.stringify({msg: verboseTag + '@watch refStoreTest.getters.get()', newVal: newVal, oldVal: oldVal});
        console.log(msg);
        newVal = newVal.sort((a,b) => a.order-b.order);
        this.refStroeRecentlyHoveredGeometriesProps.setters.set(newVal); 
    });
}

Solution

  • As @EstusFlask and the documentation point out, the $watch config needs to be the third argument:

    • first arg is what to watch
    • second arg is the callback function (gets run when the first arg changes)
    • third arg is the watch config

    In your case:

    this.$watch(
      // first arg
      this.refStoreTest.getters.get,
      // second arg
      (newVal, oldVal) => {
        const sorted = [...newVal].sort((a, b) => a.order - b.order)
        console.log(
          JSON.stringify(
            {
              msg: verboseTag + '@watch xcx refStoreTest.getters.get()',
              newVal,
              oldVal,
              sorted
            },
            null,
            2
          )
        )
        this.refStroeRecentlyHoveredGeometriesProps.setters.set(sorted)
      },
      // third arg
      { deep: true }
    )
    

    Side notes:

    • you have a syntax problem: in JS one has to declare variables before they can be used. This means you should prefix msg = 'foo' with either const (if never reassigned) or with let (if reassigned).
    • naming is important; naming a getter get does not provide context or usage information about the value and will likely make the code harder to read for other developers coming to the codebase or even to yourself, when returning to it a few months or years later. I'm mentioning it as it's often a silent selection criteria for interviewers, even when not clearly specified. Even for basic test functions, naming them test, foo or logger will project a more seasoned approach to coding.