I think it is a shame that the Observable proposal [1] still seems somewhat stuck in Stage 1. It's a better idea than just raw event emitters because of composability (if no other reason). Making Observables "first class" could go a long way to unifying a lot of reactivity patterns in various frameworks, in theory at least.
To be fair, Observables and especially Observable composition has a rough learning curve and many frameworks like Svelte intentionally prefer implict reactivity and avoiding things like explicit Observables because they are seen as too complex/"too hard" for the average developer.
(Then you get awful worst of both worlds frameworks like Angular that sort of rely on Observables but yet also don't trust teaching Observables and wind up with code that isn't properly Observable and so also has all the code for implicit reactivity and is full of nasty escape hatches that cause all sorts of composition problems and unnecessary side effects.)
I really, really love Observables as an imperfect solution in an imperfect world, but I don't think they will solve reactivity.
The main issue is, that it becomes a nightmare to transmit and compose meta-information. An example would be the fetchStatus of ReactQuery [1].
I way too often end up in situations like this:
.map {
unwrapMetaData
...
rewrapMetaData
}
...
.map {
unwrapMetaData
...
rewrapMetaData
}
.switchMap {
// unwrap metadata and wrap it in an observable
combineLatest(...) // have fun juggling an array of nested monads :)
}
I wonder if it is possible to create a mixture between React hooks and async/await. Since you tend to work in one big scope, you could ignore the meta-info until you need it.
async live function() {
const { value, meta } = observe getValue(...) // suspends when loading, throws on error
// work with values
const multipleValues = observe Observe.all(...map(i => ...))
// more work
// evaluateMetadata
}
You can already combine hooks with something like Rx using the useSyncExternalStore hook.
I’ve worked on very complex Rx code bases. I like Rx, but it gets it be a huge pain the debug when you have very complicated chains. I haven’t used hooks too much, but from what I’ve seen the default behavior is to “do the wrong thing” which makes them very annoying to work with. The implementation of hooks is also very hacky. Like you can’t create a hook inside an if statement. That’s crazy.
I've had some success with writing Async Generator functions in mixed RxJS/IxJS pipelines.
async *function someMapOperation() {
const { value, meta } = await getValue(…)
doSomething(meta)
yield value
for await (const { value, meta } of AsyncIterableX.from(mergeAll(…).pipe(takeUntil(…)))) {
doSomething(meta)
yield value
}
// …
}
(I've done similar things in C#, as well, with its Rx/Ix pair.)
You are right, there are still good cases where being able to write the "state machine" of a complex flow as an async (generator) function makes it a lot easier to reason with. The nice thing is that Observables work well with AsyncIterables and a pipeline doesn't have to be just one or the other.
Svelte Stores: observables without all the boilerplate. If you want to do manual subscribing and unsubscribing, you can of course. Stores really are quite flexible if you really want to dive deep into them.
Certainly I've had a lot of fun with wiring mobx up to various things that provide render functions (react with mobx viewstate classes is way nicer than hooks to me).
Svelte I've never quite got the hang of to the point where I feel comfortable expressing an opinion about it (and I haven't spent enough time experimenting to claim my not having got the hang of it yet means anything either).
Mobx as your ViewModel/Model selector implementation is precisely where it belongs in your stack. You just need to be disciplined and not build your whole state/update loop on it. Sadly, I'm stuck on v4.5.x at work, and my big complaint is arrays not being a first-class observable.
> You just need to be disciplined and not build your whole state/update loop on it.
If you have a minute, I'd love you to expand on that. I have some thoughts along those lines myself but I suspect you've spent more time with mobx than I have and would rather hear yours from cold.
(I also keep looking at mobx-state-tree and wondering if that would work for me, I'm not sure I know enough to judge what the trade-offs involved are particularly well)
To be fair, Observables and especially Observable composition has a rough learning curve and many frameworks like Svelte intentionally prefer implict reactivity and avoiding things like explicit Observables because they are seen as too complex/"too hard" for the average developer.
(Then you get awful worst of both worlds frameworks like Angular that sort of rely on Observables but yet also don't trust teaching Observables and wind up with code that isn't properly Observable and so also has all the code for implicit reactivity and is full of nasty escape hatches that cause all sorts of composition problems and unnecessary side effects.)
[1] https://github.com/tc39/proposal-observable