nx-js / observer-util

Transparent reactivity with 100% language coverage. Made with ❤️ and ES6 Proxies.
MIT License
1.2k stars 94 forks source link

Not sure I'm understanding this at all #42

Closed jameslkingsley closed 5 years ago

jameslkingsley commented 5 years ago

In my class constructor I'm trying to observe an object that's passed in, like this:

observable(this.form)

observe(() => {
    console.log(this.form)
})

But when I change the form properties nothing is logged.

I'm also not understanding how the observe callback even runs - how does it know we're listening to the this.form observable? Surely it would be simpler to do it this way:

observe(this.form, ({ value, path }) => {
    // this.form has changed
})

Thanks

solkimicreb commented 5 years ago

observable is returning a wrapper Proxy around the passed object. It is not doing anything with the original object. Your above example should look like this:

const observableForm = observable(this.form)

observe(() => {
    console.log(observableForm.someField)
})

And then when you change someField the observer will automatically run.

In case you use multiple fields of the observable the observer runs if any of those are mutated.

// this runs when someField or otherField is changed
observe(() => {
    console.log(observableForm.someField + observableForm.otherField)
})

Any property access or mutation at any depth is picked up and causes the relevant observers to rerun. console.log is a special edge case. It is a method that does not use JS to access the object's properties but uses some internal code in most platforms. This means that the observer-util can not pick up property accesses by console.log. You can do this for your use case:

observe(() => {
    console.log(JSON.stringify(observableForm))
})

JSON.stringify is implemented in plain JS and the observer-util can pick up any property access that happens inside it.

If you are interested in how this library works you should read this article which explains it through a very light React wrapper.

I hope this helped.

jameslkingsley commented 5 years ago

Thanks, that makes sense! What happens if I wanted to have two observable objects, like this?

const observableA = observable(a)
const observableB = observable(b)

observe(() => {
    // Is this A or B that's changed?
})
solkimicreb commented 5 years ago
const observableA = observable({ prop1: 1, prop2: 2 })
const observableB = observable({ prop1: 12, prop2: 'Hello' })

observe(() => {
    console.log(observableA.prop2 + observableB.prop1)
})

The observed function would run when observableA.prop2 or observableB.prop1 are changed since they are used by the observed function. It would not run when observableA.prop1 or observableB.prop2 are changed since they are not used inside the function (and wouldn't result in a different function effect).

jameslkingsley commented 5 years ago

Interesting, thanks for explaining it to me!