selsamman / proxily

Proxily is a library for managing state in a non-prescriptive way. It re-renders components as state changes. While Proxily does not use immutable state it provides many of the same benefits. There is no need to annotate or describe the state shape as Proxily will discover it as it navigates through the state hierarchy.
MIT License
1 stars 0 forks source link

Arrow functions on class do not work. #6

Open seivan opened 2 years ago

seivan commented 2 years ago

Assume this code

class CounterState {  // Your State
    value = 0;  
    increment () {this.value +=1 }
}

const state = observable({ 
    counter: new CounterState()
});

const Counter = observer(function Counter({counter} : {counter : CounterState}) {
    const {value, increment} = counter;
    return (
        <div>
            <span>Count: {value}</span>
            <button onClick={increment}>Increment</button>
        </div>
    );
});

function App () {
    return (
        <Counter counter={state.counter}/>
    );
}

If you change to increment () {this.value += 1} to increment = () => {this.value +=1}

It will no longer trigger updates.

I'm no expert, but isn't it better that arrow functions work since you've always expecting to get the correct this, especially in a class.

selsamman commented 2 years ago

For the proxying needed to support observable behavior all references to objects must stem from the object that is returned from observable. The object returned is a Proxy. At the time the object is made observable, observable behavior is cascaded to all referenced properties by making them a Proxy. This is why when modifying data in the constructor observable behavior is extended to the modified properties.

In the case of a normal method "this" would be bound to the object when referenced as object.method. Proxily goes further by binding the method to the proxied object so you can use it without the object.method notation. In the case of an arrow function, "this" is not bound to the object but rather assumes the value it had during the course of execution of the constructor. Since the constructor of the class is executed before the object is made observable "this" will have the value of the original object rather than the proxied object. Therefore it won't have observable behavior.

As to whether or not it is "better" to use arrow functions rather than normal methods I suppose it is a matter of opinion. Arrow functions inside class methods are essential to ensuring that "this" will inherit it's value from the method itself. These, however, are normally local functions within the method. Exposing the arrow function outside of the method as would be the case when assigning it to a property and using it in place of a method is a very different use-case in my view. I am not sure there are any advantages of using arrow function properties over normal class methods. Also normal class methods are attached to the prototype so they don't have to be created for each instance.

In any event you have identified that there are some things in the "magic" of cascading observable behavior that may not be obvious and I will attempt to address this in the documentation as any user of the library would rightfully perceive this issue as a "gotcha". Again thanks for taking the time to kick the tires on Proxily and write up your findings.