Slightly confusing api, no structural comparison, no adapters for popular frameworks (e.g. S.js-react or S.js-preact), no laziness (computations are recomputed even if they're not requested by reactions).
I don't want to use surplus because e.g. I want to use well developed UI components or toolkits like blueprintjs, which is implemented on top of React.
Slightly confusing is a real objection. MobX strives to implement transparent reactive programming, where the way you access, update and transform values works exactly like it would with regular objects. S.js has a worse learning curve.
> MobX strives to implement transparent reactive programming, where the way you access, update and transform values works exactly like it would with regular objects. S.js has a worse learning curve.
Hardly: reactive values are get/set functions, and you create new ones with S(() => ...). That's it.
MobX's attempt at transparency yields inescapable and surprising corner cases.
> Redundant computations are computed; [...] even though the completed() computed isn't used anywhere, the reduction is still being recomputed every time todo state changes
Incorrect. The todos binding is an SArray not a regular array [1]. See my modified version where I log the events to the console [2].
The number of tricky corner cases in MobX are very few. Also, once the implementation switches to proxies the vast majority will disappear.
I'm willing to accept a few corner cases as long as there is a large common subset of functionality that works both with and without a small number of decorators. This can be utilised to write models that can be used in both a reactive and a non-reactive context with a different set of decorators injected in. S.js looks too invasive to do this.
Your codepen has no completed todo count. Why are the recomputations logged every time here?
> Your codepen has no completed todo count. Why are the recomputations logged every time here?
Because you can't apply reduce any other way. It's a computation defined over a whole collection. To incrementalize it, you'd need to be able to invert whatever function you're trying to apply in order to arbitrarily undo and redo the operation as elements are added/removed. This is literally impossible in general as not all functions have inverses.
We use this to great extent in our application, by only rendering components that are visible in the viewport at the moment.
You can also keep its cached value alive, but not recompute it until needed by implementing a reaction that observes a computed but doesn't request its value. This will keep the entire computation graph cached but idle and partially dirty until the value is requested, at which point only stale dependencies will be recomputed. This can be extremely powerful: for example you can implement a state tree undo/redo by implementing serialize, then observing the serialize computed for the root item without requesting its value (keeping things cached) and only requesting recomputations when certain sufficient number of mutations are made. (with the vast number of reused values being structurally shared between undo/redo states)
> What would you call it? S.atomic? I don't see how the existing name is particularly unsuitable.
Yes atomic would be an improvement. Freeze only makes sense if you are thinking in terms of FRP signals in time, and its unclear whether the abstraction tries to hide its signal underpinnings or expose them (its somewhere inbetween)