sveltejs/rfcs

About using TC39 observables as store

Closed this issue · 2 comments

Hi,

Out of interest I read part of the Svelte RFC particulary about state management. There I read about objections of using TC39 observables as store.
In this issue I hope I can nuance those objections a bit.

They don't represent a single value changing over time, but rather a stream of distinct values.

The most basic mode of observables is that they simply are a placeholder of a function (not state) which is called when you subscribe it. This makes them cold by default, yes.
But you can simply make an instance of an observable that represents current state. new BehaviorSubject('current state') acts very much like the writable example.
You can also make cold observables hot and let them act like having state by using the shareReplay(1) operator.

Two different subscribers to the same Observable could receive different values (!) ...

Not when they are hot (based on subject)
Furthermore, this is up to the developer whether he wants it to act in this way. A dev using TC39 observables should know the difference between hot and cold observables.
Actually, Observable is just a contract, which can behave like how a dev wants it to behave. They can behave exactly like stores in this RFC.

Observables can 'complete', but declarative components (in Svelte and other frameworks) deliberately do not have a concept of time. The two things are incompatible

A completed observable simply doesn't emit new values. Svelte can ignore this concept. Also a dev can choose to never complete an observable.
Also I don't understand what you mean by 'do not have concept of time'? State is usually something that changes over time. Completing simply means it doesn't change any longer...

They have error-handling semantics that are very often redundant (what error could occur when observing the mouse position, for example?).

When observables are used to represent current state (behavior), errors are (usually) redundant yes. Svelte can ignore that like 'completed'.

When they're not redundant (e.g. in the case of data coming over the network), errors are perhaps best handled out-of-band, since the goal is to concisely represent the value in a component template

In that case, the observable is usually not used as a behavior in templates (directly), but more as an async task like promise that can return a stream of values.
The nice thing is that in that case they can be converted to a behavior later because they have the same contract.
Also because the different usages share the same contract, they share the same rich set of operators too.

As an example, the 'derived store' example could be rewritten using a single existing operator: combineLatest.
A redux store can be rewritten like this:
const store$ = action$.pipe(scan((state, action) => reduce(state, action))
Where action$ can be a merge() of different action emitters.

I like the idea that the different modes can be modelled the same, so code and ideas can be shared, and devs have to know less to be able to interact with more frameworks.

I'm not sure what the thrust of this issue is. The Svelte store contract is documented here https://svelte.dev/docs#Store_contract which also describes the interoperability with RxJS Observables.

@Jopie64 The "Reactive stores" RFC (#5) was resolved, and the store contract in Svelte v3 reflects the current state of things.