Synapsev6.0.0

Reactive reads & controlled re-renders

TL;DR. You mutate a storage with ordinary methods (set/update) and read it reactively inside a component. There are five tools, and they are easy to confuse — this page is about which one to pick. Quick answer: by default useStorageSubscribe (no RxJS); if you need operators — useStorageObservable or toObservable + useObservable; if you need a side effect (toast/log) — useSubscription; if you read a SelectorAPIuseSelector. The examples use the end-to-end todoStorage (TodoState = { todos: Todo[]; filter: Filter }).

Why

The difference between the tools is along two axes: whether you need RxJS (operators debounceTime/scan/…) and what you do with the stream (render a value or run a side effect). Below is the choice along these axes.

What to pick

ToolWhereRxJSYieldsUse whenPage
useStorageSubscribeReactnoa slice value into renderdefault reactive read
useSelectorReactnoa SelectorAPI value into renderyou read a memoized selector
useStorageObservableReactyesa slice value into renderyou just need a slice via RxJS
useObservableReactyesany Observable's value into renderyour own pipe(...) over a stream/selector.$
useSubscriptionReactyesnothing (side effect)toast/log/dispatch on each emit
toObservableoutside Reactyesan Observableeffects, non-React code
getStateSync()anywherenoa value once, no re-renderread the latest in a handlersee below

How it all fits together:

  • You don't need RxJS and want a value in renderuseStorageSubscribe (from a store) or useSelector (from a SelectorAPI). 90% of cases.

  • You need RxJS operators → first toObservable(storage, selector) builds the stream; then in React it is subscribed by useObservable (a value into render) or useSubscription (a side effect). useStorageObservable is sugar over toObservable + useObservable for the "just a slice without your own operators" case.

  • Outside React (effects, watchers, non-React modules) → only toObservable.

Reading without a re-render is not a hook

A common case is "read the current value at the moment of a click/submit without re-rendering the component on every store change". You don't need a dedicated hook for that: a storage is read synchronously on demand via getStateSync().

typescript
// zero subscriptions, zero re-renders — the fresh value at call time
const onSave = () => {
  const { todos } = todoStorage.getStateSync()
  api.save(todos)
}

If you want a re-render only when a specific slice changes, that's useStorageSubscribe with equals (Concurrent-safe), not a manual force. If you need operators (debounceTime, scan, …), that's useStorageObservable / toObservable. There is deliberately no "ref hook with a manual re-render" in the API: all three scenarios are covered by the tools above.

When you don't need it

  • You need the value once, no reaction to changesgetStateSync() / get(), see Reading data. Reactive hooks here only spawn extra subscriptions.

  • Logic outside React and without streams (a plain handler, not an effect) → the low-level storage.subscribe(selector, cb), see Subscriptions.

See also

  • useStorageSubscribe — the default reactive read.

  • useStorageObservable / useObservable — the same, but with RxJS operators.

  • useSubscription — a side effect on each emit (without render).

  • toObservable — a state stream outside React (effects, non-React code).

  • SelectorscreateSelector + useSelector.

  • Subscriptions — the low-level storage.subscribe.