nx-js / observer-util Goto Github PK
View Code? Open in Web Editor NEWTransparent reactivity with 100% language coverage. Made with ❤️ and ES6 Proxies.
License: MIT License
Transparent reactivity with 100% language coverage. Made with ❤️ and ES6 Proxies.
License: MIT License
First of all I wanna say sorry for asking so many questions. I'm sure I've overwhelmed you and I apologize for it, it won't happen again. This mostly should be all the remaining questions I have.
I'm trying to speed up my observes by batching stuff together in a more optimal way.
I experimented with queue and set and found them both really useful.
I noticed that if I setInterval Set it always runs once the first reaction is fired. And doesn't stop either (i guess unless I manually stop it)
I was curious if their was a way to combine the automatic process of priorities, with the interval from set. I wanted to try making the priority low, and when it does fire it can only if the interval allows it.
Lastly I have each instance placed in the map set too observe its own observable self from the store to update properties. (this was carried from my mobx way of doing it)
I noticed that this way is very slow in this library compared to mobx so I assume its not batching in the same way and I'm doing something wrong here(I may have been doing wrong all along anyway)
Is there an easy way to batch the process of observing itself with other instances doing the same type of observing?
Or should I have this type of observable process done in a loop through all the instances outside the instance, rather then in the instance itself.
Thank you for taking the time to explain this stuff for me this is a really incredible library.
I am trying to pass an observable between two web components (e.g. a parent and a child). I was hoping to put a reaction in the render
method of both components based on the same observable (I am passing the observable from the parent to the child on the HTMLElement.data property). Then if the observable was modified by either component, it would re-render either/both as appropriate.
What I am seeing however is that the reactions only fire in the element where the observable is created. Is this the intended behavior?
Why this behavior?
import { observable, observe } from "@nx-js/observer-util";
// This WORK
const counter = observable({ num: 0 });
const countLogger = observe(() => console.log("a-", counter.num));
// this calls countLogger and logs 1
counter.num++;
setTimeout(() => {
// this calls countLogger and logs 4
counter.num = 4;
}, 2 * 1000);
// This NOT WORK
const counter2 = observable({ num: 0 });
const countLogger2 = observe(() => console.log("b-", counter));
// this calls countLogger and logs 1
counter2.num++;
setTimeout(() => {
// this calls countLogger and logs 4
counter2.num = 4;
}, 2 * 1000);
// Output
// a- 0
// a- 1
// b- Object {num: 1}
// a- 4
Why does observing counter
instead of counter.num
break the behavior?
Here is a sandbox
was reading through the code to see how observable is implemented with proxies. I saw this line https://github.com/nx-js/observer-util/blob/master/src/handlers.js#L28 and thought rawToProxy
should work as a cache to avoid recreating proxy if some nested property is accessed multiple times. but after search by rawToProxy
I could not find any set
call.
I understand that it only affects perfomance but not affect the correcntess of behavior
edit: seems same is for proxyToRaw
Currently, it is not possible to observe the size
property on Maps and Sets:
const map = observable(new Map())
observe(() => console.log(map.size)) //0
setTimeout(() => map.set('key', 'value'), 200)
// crickets...
This issue is a response to this article by Michel Weststrate, which is a response to this article by me. I decided to not write another one, but try to spark a conversation about reactive programming here. I hope that this discussion might produce useful and new reactive programming ideas.
NX goals
This is a list of what NX tries to achieve and hopefully does currently achieve.
get
(reaction registration) and set
(reaction triggering) like language operations are picked up.A kind request to transparent reactivity experts: if you think, that any of the above points are not working correctly in NX please provide a small example snippet.
About stale values
There is no computed
in NX, you should use getters/setters instead. Check the following example.
const nx = require('@nx-js/observer-util')
const person = nx.observable({
firstName: 'Dwane',
lastName: 'Johnson',
get fullName () { return `${this.firstName} ${this.lastName}` }
})
// outputs 'Dwane Johnson' to the console after the current stack empties
nx.observe(() => console.log(person.nickName || person.fullName))
// outputs 'Dwane Bob' to the console
setTimeout(() => person.lastName = 'Bob')
If person.fullName
is used anywhere in the code (in normal code or reaction), the getter function executes and produces a value. Value is never cached by NX and NX never ever stores data, it only pairs reactions with state property keys. As data is never stored or modified by NX, I don't think that it could cause stale data.
About the derivation dependency graph
I am still very uncertain about this, so if I miss something important please enlighten me. The following section explains how I currently think about this problem.
I don't see the reason for keeping track of a whole dependency graph. Keeping track of the state property - used by a reaction - and the top level reaction (wrapped in observe
or autorun
) seems enough to me. NX simply keeps track of these pairs. If the observed state properties change later, the belonging top level reactions are triggered by NX and they call the getters (computed values) and other functions in correct order.
The graph feels like a reconstruction of a stack to me, which is already defined by the current app state and the first function of the stack (saved top level reaction) anyways.
For a more detailed explanation and a working sub 100 lines prototype see this article. The actual NX logic is also around 100-200 lines and it can be found here. The rest are just platform and Proxy specific bug fixes and workarounds.
About $raw
The $raw
property in NX does actually work like untracked if it is used for get
operations in reactions. If it is used for set
operations it can work the other way too.
const nx = require('@nx-js/observer-util')
const person = nx.observable({
name: 'John',
age: 25
})
// outputs 'name: John, age: 25' to the console after the current stack empties
nx.observe(() => console.log(`name: ${person.name}, age: ${person.$raw.age}`))
// will not cause a rerun, since the observer only uses person.$raw.age
// outputs nothing to the console, similar to untracked in MobX
setTimeout(() => person.age = 30)
// outputs 'name: Bill, age: 30' to the console
setTimeout(() => person.name = 'Bill', 100)
// will not cause a rerun, since it only modifies person.$raw
// outputs nothing to the console
setTimeout(() => person.$raw.name = 'Anne', 200)
Even though age
is used inside a reaction, changing person.age
won't trigger the reaction because it used the $raw
(untracked) version of age.
The reaction used the tracked version of name
, but setting the $raw
version of later won't trigger the reaction.
Please note that the observed and the $raw
version are the same object so these two are always 'in sync'. Observables are transparent layers, that forward every operation to the raw object.
Questions
I think it should be mentioned that observe(...)
call is side-effectful. It runs the function first time immediately (to evaluate those proxies) and the reaction is almost always a side-effect like logging, mutating, with probably the only exception of doing nothing...
import { observable, observe } from '@nx-js/observer-util';
const counter = observable({ num: 0 });
+ // this calls countLogger and logs 0
const countLogger = observe(() => console.log(counter.num));
// this calls countLogger and logs 1
counter.num++;
There's a section of Reaction scheduling but it describes another aspect (of scheduling, while the first time it's always a sync run, no matter which scheduler is applied, right?).
Test case:
https://runkit.com/scriptspry/observer-utils-global-definitions-not-observable
const {observable, observe, isObservable} = require('@nx-js/observer-util');
class Counter {
constructor() { this._count = 0 }
increment() { this._count += 1 }
decrement() { this._count -= 1 }
}
const c = observable(new Counter());
console.log('c', isObservable(c));
global.Counter = Counter;
const c2 = observable(new Counter());
console.log('c2', isObservable(c2));
Hi!
Would anyone be interested in doing this. I would put it into a separate repo (like mobx and react-mobx) to keep nx-observe framework independent. If nobody is interested I will try to do it, but I am pretty much a React newbie.
it is a pain in the a$$, I know, but it is possible with a shim, even ie8 :p
I am using a library that basically thin wrap this utility: react-easy-state
.
It seems that re-assigning a new object to an observable property do not trigger the change.
Is there any special case?? Here is a simplification of what is happening to me.. I would like to have some suggestion of what can be wrong or I can debug this. Can't wrap my head around this problem:
// Component A
const A => view(() => <div>{store.nestedObject.title}</div>)
// Component B = this way DOES NOT WORK! Component A does not update
class A extends React.Component {
updateNested = () => {
store.nestedObject = object // => this is a big object I receive from server
}
render() {
return <button onClick={this.updateNested}>click me</button>
}
}
// Component B = this way WORK! Component A does UPDATE!!
class A extends React.Component {
updateNested = () => {
store.nestedObject.title = object.title // different approach that works
}
render() {
return <button onClick={this.updateNested}>click me</button>
}
}
It seems to not detect when I change the whole object
Hi, I'm just wondering on how one can integrate this into Angular 7. Like a simple component where I just a field in a text field and it updates the value in the other text field.
Thank you in advance!
What can be done if observable is applied to 3 different vars Nd need to provide observer to target variables instead of observing all 3
Thanks for a fantastic library. So elegant!
I am trying to use it in a scenario where I have another proxy in order to make the object more elegant to use. I want to create an object which acts like an array, but also stores other data. However observe() doesn't flow through the proxy. I am not surprised it doesn't work, but I am looking for ideas on how to make it work.
import { observable, observe } from '@nx-js/observer-util';
class Foo {
constructor() {
this._collection = [111]
return new Proxy(this, {
get(target, prop, receiver) {
if (prop in target._collection)
return target._collection[prop]
else {
return Reflect.get(target, prop, receiver);
}
},
has(target, prop) {
if (prop in target._collection) {
return true
} else {
return Reflect.has(target, prop)
}
}
})
}
get data() {
return this._collection
}
}
const counter = observable(new Foo);
const workingObserve = observe(() => console.log(`Working: ${counter.data[0]}`));
const notWorkingObserve = observe(() => console.log(`Not working: ${counter[0]}`));
counter._collection = [1,2,3]
counter._collection = [7,8]
Note that the notWorkingObserve
function is not executed when the collection is updated.
I am testing by just running this code in https://repl.it/languages/babel
Hi,
I just found an issue while trying to store objects on IDB. It seems that IDB can't serialize proxied objects, and throws an error (Serialization error: Can't serialize [object object] on xxx...).
So, in order for me to be able to store these objects, I have to serialize/deserialize the object manually (so effectively removing the proxy).
I was thinking that maybe you can trap the checks for "has toJSON" to return true and then when that method is invoked, return the raw object. That would solve the serialization issue.
If the object already has a toJSON method, you should not have to do anything.
What do you think?
I'm trying to observe an array of objects and see what type of change was made.
If the array had stuff removed, I wanna call a specific function to respond to that.
If the array had stuff added, I wanna call a different function.
In mobX this was rather straight forward...observe would just return an object that could tell u a list of if things changed, added, or removed...
I was curious if you had an easy way to observe a type of change.
Edit*( Not only just observe the type of change, but maybe return the values changed?)
Like if 2 objects were pushed into the array, it return added: [object1, object 2]
I think mobx had something where it give u a list of objects that changed...
So if I added 2 objects, I'd get an array of added with both objects
If I had removed 2 objects, I'd get an array of removed of both objects)
Worst case scenerio... I maybe try to cache a second copy of the array, and compare a changed version to the old version using a function to determine the type of change...
Maybe thats the intended way to do it.
Again, this library is amazing. Thank you so much for how easy it is to use!
Is there a reason for why this limitation exists? I can see how this may cause loops, but otherwise there's nothing wrong with having reaction triggered in reaction (not in, after).
It would be nice to make sure the documentation is usable by everyone:
I'm confused about the behavior of the following code:
const list = observable(['Hello'])
observe(() => console.log(list))
list.push('World!')
This logs ['Hello', 'World!']
twice. I expect it to log once. I get expected behavior from:
const list = observable(['Hello'])
observe(() => console.log(list.concat()))
list.push('World!')
Is this the correct behavior?
Hi! Thanks so much for the library, it's been great to use.
We've been trying 4.3.0-alpha.2 to get the react-easy-state fix RisingStack/react-easy-state#184, and ran into a problem using constructors that have been observed -- at least with @babel/plugin-transform-classes. I haven't been able to test without it, but either way the behavior doesn't seem right.
An example might be easiest:
class Thing {}
const thing = new Thing(); // => Thing instance
const observableThing = observable(thing); // => Proxy wrapping Thing instance
const Constructor = observableThing.constructor; // => Proxy wrapping Thing()
const newThing = new Constructor(); // => Fails with Uncaught TypeError: Cannot call a class as a function
The failure happens because the construct
function here is called as
construct(Thing, [], Proxy { Thing() })
When constructing the object, Thing()
is called with this
set to Proxy { Thing() }.prototype
, which is also a Proxy. This means that this instanceof Thing
will be false, which seems incorrect.
I think using the raw
constructor as newTarget
would be the easiest fix, but I don't know whether that's right.
I would like to be able to observe for any change in a deep object.
@solkimicreb, would it be difficult to expose an ignore
function that operates identically to raw
within an observer but without exposing the actual raw object?
this would allow me to avoid the unwrapping problem mentioned in #18 and the vulnerabilities that come with unwrapping. see lukeburns/immutator#2 for some more details.
in short, it would nice to prevent reactions from triggering while still preserving the properties of proxies that wrap observables:
let { observable, ignore } = require('@nx-js/observer-util')
let state = anotherProxy(observable({ time: 0 })
observe(() => console.log(ignore(state.time))) // triggers only once on initial run
setInterval(() => state.time++, 1000)
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?
React-Native doesn't support Proxy at the moment. We have been trying to use proxy-polyfill, but it doesn't support the entire capabilities of native proxy. Wanted to take your opinion on what could be broken as a result of this.
Thanks!
i'm exploring the possibility of implementing the maximum feature set of observer-util compatible with https://github.com/GoogleChrome/proxy-polyfill, which only works for get, set, apply, construct traps.
it's severely limited. it would restrict use to observing predefined properties (as the polyfill is unable to intercept new properties), and handling arrays would be slow (it would require replacing the entire array every change), but i think there are use cases where developers can work under reasonable constraints that would allow at least for a working implementation in e.g. IE11, even if slow.
i understand this library has opinion and likely no interest in supporting IE, but if i find myself interested in such an implementation, i wonder what challenges you would anticipate.
I want to take a bunch of observable/streams (like RxJS, I don't know the exact name of those) and pass them to a function that will return an observable
that will have all their last emitted values stored, and every time one of those observable/streams emit something that will update the observable
and trigger updates accordingly to observers
.
This seemed like a simple thing to do, but I'm somehow seeing a lot of unexpected errors.
import { observable as nxobservable } from '@nx-js/observer-util'
export function observable (def = {}) {
var values = nxobservable({})
for (let property in def) {
let value = def[property]
setnew(values, property, value)
}
return new Proxy(def, {
set (target, property, value) {
setnew(values, property, value)
},
get (target, property) {
return values[property]
}
})
}
function setnew (target, property, value) {
var stream = value
var initialValue
if (Array.isArray(value) && value.length === 2 && value[0].subscribe) {
stream = value[0]
initialValue = value[1]
}
if (!initialValue && stream && stream._prod) {
// hackshly inspect xstream streams to get initialValues
switch (stream._prod.type) {
case 'fromArray':
initialValue = stream._prod.a[0]
break
case 'startWith':
initialValue = stream._prod.val
break
case 'mapTo':
initialValue = stream._prod.f()
break
case 'debug':
setnew(target, property, stream._prod.ins)
}
}
if (!value || typeof value.subscribe !== 'function') {
initialValue = typeof value === 'object' && !Array.isArray(value)
? observable(value)
: value
stream = noopStream
}
target[property] = initialValue
stream.subscribe({
next (v) {
target[property] = v && typeof v === 'object' ? observable(v) : v
},
error (e) {
console.error(`error on stream ${property}:`, e)
target[property] = e
}
})
}
const noopStream = {subscribe: () => {}}
There's a lot of complication here because I had to introduce initialValue
, which wasn't needed when I was trying to build this from scratch without using Proxy
or @nx-js/observer-util
(in my previous implementation the observers were only triggered when the streams emitted their first values, but now they're triggered before that.)
I'm just looking for some help, but if there was a way to "intercept" values before get
I think it would solve it to me.
https://github.com/tylerlong/choose-a-state-container/blob/master/cache/observer-util.spec.js#L24
It should have cache. Otherwise what's the point of a computed property? It is just a normal method without cache.
Like the reselect sample: https://github.com/tylerlong/choose-a-state-container/blob/master/cache/redux.spec.js#L40
And the SubX sample: https://github.com/tylerlong/choose-a-state-container/blob/master/cache/subx.spec.js#L24
I don't understand why I am getting this error. The code where I am mutating the observable is not in a reaction? Here is a sample of what is happening in a custom element.
// create observable store
this.store = observable({
clickCount: 0,
clickTimestamp: null
});
// re-render component with async/dedupe Scheduler
observe(() => {
this.renderCallback();
}, {scheduler: new Scheduler()});
// event handler mutating props
this.addEventListener('click', (e) => {
this.store.clickCount = this.store.clickCount + 1;
this.store.clickTimestamp = Date.now(); // <== I GET THE ERROR HERE
})
I am getting the error trying to set the second property on the observable.
If 2 different modules require this lib like so
const observer = require("@nx-js/observer-util"); //module-a
const observer = require("@nx-js/observer-util"); //module-b
can they both observe the same observable? It doesn't seem like it, since currentObserver
would be different in both scenarios. I was forced to pass a singe observer
object around. This seems like a big limitation, am I missing something obvious, or is there an elegant way you handle this inside nx-framework?
Hi,
this is not an issue, but a question. In the documentation, you are saying
Reactions are functions, which use observables. They can be created with the observe function and they are automatically executed whenever the observables - used by them - change.
I read this post and looked into the code, but was not able to find out how the observables used in observed functions are detected. When I have used a similar approach in C# it involved AOP tool and analyzing the code of the method.
Are all observers executed whenever any observable changes? Or you trigger the function and detect which observables are read?
Thanks for clarification,
Karel
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
Should properties with Symbol keys trigger observed functions, when they change value?
Currently Symbols are observable with the exception of well-known Symbols. The reason is that well- known symbols are frequently used internally by JS, which would lead to unexpected observed function triggers and performance issues.
The more I use the observer-util the more I feel like this should be extended to all Symbols. Symbols are generally used to store internal low-level data, which should not be observed.
What's your opinion? Is there anyone who relies on observing Symbol keyed properties?
The following scenario doesn't work (explicitly deleting items by modifying array.length)
it("should observe explicit array length changes", () => {
let dummy;
const list = observable(["Hello", "World"]);
observe(() => {
dummy = list[0];
});
expect(dummy).to.equal("Hello");
list.length = 0;
expect(dummy).to.equal(undefined);
});
I came across a problem using react-easy-state that feels like a limitation in observer-util.
To make sure that React re-renders when an object assignment changes, the object needs to be observed. But observing properties inside the object can have negative effects. Observing the object "pollutes" everything inside it by wrapping properties in proxies and propagating the proxies deeper and deeper into the hierarchy of nested objects as the object mutates over time.
Imagine the object comes from an unrelated Javascript library and cannot tolerate the performance impact of proxy observers on every nested property or object. In addition adding proxies prevents object equality checks from working when a proxied version of the object is compared with a non-proxied version. Again, because this is in a third-party library it is not possible to use raw
to fix the equality.
It seems like the only solution would be to have a kind of "shallow" observer that only observes the object itself (or perhaps the container of the object), and none of the object's properties. It is not completely clear to me how this could work in a way that was not too burdensome on the programmer. I am wondering if you have thought about this problem before and have any ideas for solutions.
Was doing testing in Edge 14, and found that observer-util doesn't queue reactions: the reason is Edge doesn't implement WeakMap properly chakra-core/ChakraCore#2419 and the set
handler returns before queueing at https://github.com/nx-js/observer-util/blob/master/src/handlers.js#L80. Just putting this here in case it's helpful to anyone else targeting older Edge versions.
I am having a difficult time reasoning how using this library will work when I am passing data down a stack of web components.
Let's say I have an array of items at a top-level component that I have wrapped as observable. I need to pass that array down to child components (through a JavaScript property). The child component also wraps the property as observable.
a) Since I want to keep the web component interface agnostic of using the proxy for reactivity, should I be passing the raw object down to the child components rather than the proxied object?
b) Will duplicate proxies be created for each web component as the array is passed down, or does the library detect its the same object and reuse a single proxy?
c) If I change the array at the high-level component, will it only trigger the observe() within that web component, or will the observe() methods fire at every web component I passed the array down to automatically as well? (I am concerned that it fires at every level, but then also fires again as the stack is re-rendered down from the top-level component).
I am hoping I am explaining this correctly - this is a difficult scenario to describe but unidirectional data flow is a proven architecture and something I really need to work with native web components.
In this code, "this.items" is an observable array, that contains items that are observables themselves.
When I run this code:
moveItem(from, to) {
if (from === to) return;
// this algorithm keeps the array length constant
const itemToMove = this.items[from];
if (to > from) {
this.items.copyWithin(from, from + 1, to + 1);
} else if (to < from) {
this.items.copyWithin(to + 1, to, from);
}
this.items[to] = itemToMove;
}
then all the items that are affected by the copy operations, that is, items affected by copyWithin() and also items affected by plain assignments, like in this.items[to] = itemToMove
where itemToMove is a proxy, get replaced by their raw items inside the array. So what started oput as an array of proxies ends up as a mixed array of proxies and raw items.
I found one workaround by operating on the raw array first, but it is not elegant:
moveItem(from, to) {
if (from === to) return;
const items = raw(this.items);
// this algorithm keeps the array length constant
const itemToMove = items[from];
if (to > from) {
items.copyWithin(from, from + 1, to + 1);
} else if (to < from) {
items.copyWithin(to + 1, to, from);
}
items[to] = itemToMove;
// we made changes on the raw array, because copyWithin and assignments applied to the proxy
// 'this.items' strip the copied/assigned array elements of any proxies that might wrap them.
// So we need to trigger a reaction explicity on this.items by clearing and re-assigning to it.
this.items = [];
this.items = items;
}
Am I missing something? Is there a better way to do this?
.observe()
callback is not called if the changed property getter is not executed in the .observe()
callback. Take a look at the example below. Execute it then try uncommenting the line and commenting the one above.
const observer = require('@risingstack/nx-observe');
const observable = observer.observable();
// This way doesn't call the callback twice
observer.observe(() => console.log(observable));
// This way works
// observer.observe(() => console.log(observable.value));
observable.value = 3;
setTimeout(() => observable.value = 4);
I have been using version 4.1.3 without any problems, but when I tried 4.2.0 my model updates were not propagated downwards anymore. Is there something that changed when render() is called? reverting back to 4.1.3 fixed the issue.
I have code like this
<InputPassword changes={v => loginModel.password = v}
name="password"
value={loginModel.password}
big={true}/>
and my model is imported from
const loginModel = observable(new LoginForm())
export {loginModel}
I receive the new v
values but when the component re-renders value hasn't changed.
I use it with preact but it's basically almost the same as react-easy-state
Any idea what is breaking it?
I have an observables array that I observe to listen to changes.
On size change I update the information based on if it changed size.
Increasing the array with push seems to work fine. It reacts properly.
However splicing or poping it (reducing it's size) calls the reaction twice... This second reaction returns the same length array but with the value set to null in the position of where the pop happened (and I assume where any element removed)
This is causing me a lot of issues in being able to observe arrays changes....Ive had to resort to manually doing reactions...tho I'd prefer if it was automatic based on observe.
I read somewhere in another issue that arrays do weird things with proxy and observables...so I was curious if this could be fixed.
Hi. I'm experiencing an issue when using react-easy-state, however the TypeError is coming from this lib so I thought it best to report issue here. This may not be a bug but rather a lack in my understanding of how proxies work or what limitations they may have, or perhaps the issue lies in the Tone.js library classes I'm trying to proxy, in any case here is an example that I think will illustrate the issue better than I can explain:
import React from "react";
import { render } from "react-dom";
import { store, view } from "react-easy-state";
import Tone from "tone";
const osc1 = new Tone.Oscillator();
const osc2 = new Tone.Oscillator();
const proxy = new Proxy(osc1, {
get(...args) {
return Reflect.get(...args);
}
});
// works
console.log(proxy.frequency.value); // => 440
const oscStore = store(osc2);
// throws TypeError: Illegal invovation
const App = view(() => <div>{oscStore.frequency.value}</div>);
render(<App />, document.getElementById("root"));
In the example would expect oscStore
to behave exactly like osc2
object or even the proxy
object.
I am building native web components with Lit-Html and want to minimize unnecessary re-rendering calls. I am not sure I quite understand how the scheduler feature works. I have created a Scheduler that uses a Set, as per the example to eliminate duplicate reactions, and setTimeout
to create a task that defers processing.
class Scheduler {
constructor() {
this.timer = null;
this.reactions = new Set();
}
add(value) {
this.reactions.add(value);
if (this.timer) clearTimeout(this.timer);
this.timer = setTimeout(() => {
this.reactions.forEach(reaction => reaction());
});
}
delete(value) {
this.reactions.delete(value);
}
}
This seems to work, and results in batching reactions and generating a single render. Is this a suitable way to accomplish this, or is there a simpler way?
FYI - the delete method never seems to get called.
Setting a new value
or a new get
function using Object.defineProperty
should trigger reactions on the relevant key. This should support reacting to both modifying existing keys of and adding new keys to an object.
This would require adding a new Proxy trap and writing tests for the new behavior.
I'm willing and able to help put together a pull request.
Any thoughts?
For this snippet -
const observer = require("@risingstack/nx-observe");
let context = observer.observable({"name" : "a"});
const signal = observer.observe(()=>console.log(context.name));
let name = context.name;
context.name = "a";
The observer is triggered twice. Wouldn't it help to not trigger observers if the value hasn't changed?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.