skip to Main Content

I have a logic that need to reactively run some function

if a this.variableName is changed inside or outside the class

the problem is with this logic, getter doesn’t work, and I get undefined.

not only this, but also maximum stack error.

because is like: set the this inside the setter, that re-set the this, that re-call another time the setter, that set the this, and so on… (but this isn’t what I meant)


Semplified version, without parameters, and so, just the minimal example of the bug.

class MyClass {
  constructor() {
     this.isOn = false;
  }

  set isOn(value) {
    this.isOn = value;
    // another functions that do stuff. you can put here a bunch of console.logs if you want just for the demo
  }

  // the idea is another, but i will show you the simpliest method, I could imagine, just to make you get the idea
  
  turnOn() {
     this.isOn = true;
  }
 
  turnOff() {
     this.isOn = false;
  }

  // using this approach, 
  // I don't need to copy paste the same functions in this 2 methods, 
  // so I just need to care about the var, 
  // without using a imperative approach, 
  // but a declarative approach
}

I know we can use _isOn,

but this is literally the opposite of what we want,

because I want the setter to be the same name of the getter,

and still do the logic on setter.

hope you get my idea. thanks

2

Answers


  1. You can use private properties

    class MyClass {
      #isOn = false;
    
      set isOn(value) {
        this.#isOn = value;
      }
      
      get isOn() {
        return this.#isOn
      }
    
      turnOn() {
         this.isOn = true;
      }
     
      turnOff() {
         this.isOn = false;
      }
    }
    
    const x = new MyClass
    
    x.isOn = true
    
    console.log(x.isOn)
    
    // This will throw an error:
    // console.log(x.#isOn)
    Login or Signup to reply.
  2. As Konrad has commented, you cannot reference two distinct objects with one identifier. Instead, you could use internal members when referencing from inside.

    Alternatively, if we separate the data from the class, we could instance two objects: One where changing the data (e.g. through a proxy) has a side-effect, and the other that changes directly.

    Example:

    class MyClass {
      #data = null;
    
      constructor(data) {
        this.#data = data;
      }
      
      set isOn(value) {
        this.#data.isOn = value;
      }
      get isOn() {
        return this.#data.isOn;
      }
      
      turnOn() {
        this.isOn = true;
      }
    
      turnOff() {
        this.isOn = false;
      }
    }
    
    const data = {
      isOn: false
    };
    
    const dataProxy = new Proxy(data, {
      get(target, prop, receiver) {
        if (prop === "isOn") {
          console.log("get side-effect");
        }
        return Reflect.get(...arguments);
      },
      set(target, prop, value, receiver) {
        if (prop === "isOn") {
          console.log("set side-effect");
        }
        return Reflect.set(...arguments);
      }
    });
    
    const directObject = new MyClass(data);
    const indirectObject = new MyClass(dataProxy);
    
    console.log("Direct isOn:", directObject.isOn);
    console.log("Direct turnOn()");
    directObject.turnOn();
    
    console.log("Indirect isOn:", indirectObject.isOn);
    console.log("Direct turnOff()");
    indirectObject.turnOff();

    Here, we have two objects of class Lamp that act on the same data, one of them through a proxy. The proxied Lamp‘s methods will cause side-effects.

    This allows for one implementation for the behaviour (one Lamp class), and one set of data. The proxy allows for controlling access and (as you wanted) causing side-effects when accessing.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search