1

The set accessor does not take place whenever one does mutate the Crumbs class property value by e.g pushing into it.

I'm trying to create a class property via set syntax which I expect to handle an additional sessionStorage task. But every time, I'm going to push an item into the array, the set accessor does not take place.

The code I came up with so far is as follows ...

class Environment {
  constructor() {
    this.Crumbs = [];
  }
  set Crumbs(value) {
    // debugger;
    sessionStorage.setItem('_crumbs', JSON.stringify(value));
  }
  get Crumbs() {
    // debugger;
    let result = [];

    if (sessionStorage.getItem('_crumbs') !== null) {

      result = JSON.parse(sessionStorage.getItem('_crumbs'));
    } else {
      sessionStorage.setItem('_crumbs', JSON.stringify([]));
    }
    return result;
  }           
}
let env = new Environment();

let _metricId = 6;
let _concept = 'Back orders';

let _crumb = {
  MetricId: _metricId,
  Concept: _concept,
};
env.Crumbs.push(_crumb);
user3840170
  • 26,597
  • 4
  • 30
  • 62
Josue Barrios
  • 440
  • 5
  • 10
  • 2
    `push` won't activate the setter. You have to actually set it like `env.Crumbs = ...` – Konrad May 29 '23 at 20:28
  • @PeterSeliger Sincerely I use the first response from the comments, I appreciate your response and your time. – Josue Barrios May 31 '23 at 20:00
  • @JosueBarrios ... How do you handle or even prevent a user (from) operating the `Crumbs` array via mutating array methods like e.g. `push`, `pop`, `unshift`, `shift` and `splice`? – Peter Seliger May 31 '23 at 20:03

1 Answers1

1

None of the mutating array methods will ever activate the OP's setter. This only happens for direct assignments to Crumbs.

But since the OP wants to go with the least effort of handling array changes by any array operation, the OP needs to expose a proxyfied array as an Environment instance's crumbs property. The proxy's handler configuration has to implement just the set trap, and moreover needs to specifically handle just the change to the proxy's length property, where one would keep the stored crumbs value up to date by storing the local/internal crumbList mirror.

class Environment {
  constructor() {
    // - always "up to date" mirror of the `this.crumbs` proxy object.
    // - initialized either from `sessionStorage` or as empty array.
    const crumbList =
      JSON.parse(sessionStorage.getItem('crumbs') ?? null) ?? [];

    this.crumbs = new Proxy(crumbList, {
      set(obj, prop, value) {
        // debugger;
        // console.log({ obj, prop, value });

        // proceed with `set`.
        const result = Reflect.set(...arguments);

        if (prop === 'length') {
          // - immediately put most recent `crumbList`
          //   mirror into `sessionStorage`.
          sessionStorage.setItem('crumbs', JSON.stringify(crumbList));
        }
        return result;
      }
    });
    // - since one does access the `this.crumbs` proxy for any
    //   (mutating) array operation, one might think about a
    //   custom `valueOf` implementation which returns a shallow
    //   copy of the proxy object's up to date mirror state.
    Object.defineProperty(this.crumbs, 'valueOf', {
      value: function valueOf () {
        return [...crumbList];
      },
    });
  }
}
const env = new Environment;

const metricId = 6;
const concept = 'Back orders';

const crumb = { metricId, concept };

env.crumbs.push(crumb);

env.crumbs.push('foo');
env.crumbs.push('bar');

env.crumbs.length = 5;

env.crumbs.push('baz');
env.crumbs.push('biz');

env.crumbs.length = 9;

console.log('... after initial `push` operations and `crumbs.length` changes ...');
console.log({ "current value": env.crumbs });

env.crumbs.shift();
env.crumbs.pop();
env.crumbs.pop();

console.log('... after `shift` and `pop` operations ...');
console.log({ "current value": env.crumbs });

env.crumbs.splice(1, 3);

console.log('... after a final `splice` operation ...');
console.log({ "current valueOf()": env.crumbs.valueOf() });

console.log({ "currently stored JSON": sessionStorage.getItem('crumbs') });
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
  // mock for the SO specific stack snippet
  // due to the policies and environment are
  // not allowing an original storage access.
  const sessionStorage = (function () {

    // https://developer.mozilla.org/en-US/docs/Web/API/Storage
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
    const storage = new Map;

    function key(int) {
      return [
        ...storage.keys()
      ][parseInt(int, 10)];
    }

    function setItem(key, value) {
      return storage.set(String(key), String(value));
    }
    function getItem(key) {
      return storage.get(String(key));
    }

    function removeItem(key) {
      return storage.delete(String(key));
    }

    function clear() {
      return storage.clear();
    }

    return {
      get length() {
        return storage.size;
      },
      key,
      getItem,
      setItem,
      removeItem,
      clear,      
    };

  }());
</script>
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37