nx-js / observer-util

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

modularity and middleware with proxies #18

Open lukeburns opened 6 years ago

lukeburns commented 6 years ago

i've been wondering about what decisions can and should be made by proxy-based libraries for playing nice with other proxy-based libraries. proxies are pretty well suited for modularity, as you can extend the proxy you're working with by wrapping it with another proxy (though, i am curious how performant this is for many layers of proxies).

for instance, a logger proxy could easily be implemented separately from observer-util that lives below observables: observable(logger(state)). this is the first problem solved in redux by middleware. with proxies, there may be useful patterns to help with interoperability that don't require commitment to a particular library.

in playing with immutator and morphable, for example, i found that i needed to unwrap the immutator and observable proxy layers (i ended up choosing a different interface for accessing the underlying target than you did in observer-util). line 4 in the example for immutator is residual of this.

do you have thoughts on this broader question of the interfacing between libraries?

solkimicreb commented 6 years ago

I see you have been busy the last couple of days 😄 I will check out your libs tomorrow (going to sleep now).

Double wrapping with Proxies is not that bad and I used in my previous projects. I can't find my related JSPerf snippets, but once you use Proxies it doesn't really matter how many times you wrap from my experience. We should make a new JSPerf test with double and triple wrapping though to make sure.

Also the only thing that may be problematic with double wrapping is the receiver argument in some traps, but I think it won't cause any problems. I will add some double wrapping tests to this lib later this week.

solkimicreb commented 6 years ago

I checked your libs and they are pretty awesome, I love they fit together 👍 I am not sure I understand this part though.

in playing with immutator and morphable, for example, i found that i needed to unwrap the immutator and observable proxy layers (i ended up choosing a different interface for accessing the underlying target than you did in observer-util). line 4 in the example for immutator is residual of this.

Where did you use raw in your code after you defined it?

Another side note: I would always use the matching Reflect method in Proxy traps with all the arguments - including receiver. It makes sure to correctly forward the trapped operation (even in exotic cases of double Proxy wrapping and prototypal inheritance).

lukeburns commented 6 years ago

Thanks for the rec, I'll read up on Reflect.

_.raw is used internally by morphable to access raw state. Normally it just uses the observer-util raw function. The trouble is, by double wrapping, morphable needs to know how to unwrap the immutator proxy first. line 4 is my inelegant solution to this -- configuration for the two libraries to work together. Without it, morphable doesn't get back the raw object it expects and doesn't work properly.

solkimicreb commented 6 years ago

Oh I get it! Your solution might be more elegant actually 😄 Storing the raw object in a property instead of a WeakMap might solve the raw() - double wrapping interaction issue. I have to think about this a bit, I don't want to say something stupid.

Thanks for the discussion btw 🙂 This is something that should be solved.