# Core Concepts
# Two Worlds
To understand the design behind observable-hooks you should have two worlds in mind: the Observable World and the Normal World.
+--------------------------------+
| |
| Observable World |
| |
+--------------------------------+
+------------------+
| observable-hooks |
+------------------+
+--------------------------------+
| |
| Normal World |
| |
+--------------------------------+
These two worlds are just conceptual partition. The Observable World is where the observable pipelines are placed. It could be inside or outside of the React components. The Normal World is anyplace that does not belong to the Observable World.
# Observable to Normal
Almost every RxJS-React binding library provides ways to connect observable values to React state.
# Observable to State
In observable-hooks we have useObservableState
, useObservableEagerState
and useLayoutObservableState
.
+--------------------------------+
| Observable World |
+--------------------------------+
| |
| input$ |
| |
+--------------------+-----------+
|
|
|
v-----------+
const output = useObservableState(
| input$,
| initialOutput
| )
|
|
|
|
+--------v-----------------------+
| Normal World |
+--------------------------------+
| |
| <p>{output}</p> |
| |
+--------------------------------+
# Observable to Callbacks
In addition to states, you can also call observer callbacks with useSubscription
. See the API docs for why it is preferred comparing to manual useEffect
.
+--------------------------------+
| Observable World |
+--------------------------------+
| |
| input$ |
| |
+-------------------+------------+
|
|
|
v
useSubscription(input$, onNext)
|
|
|
|
+---------------------------v----+
| Normal World |
+--------------------------------+
| |
| const onNext = v => log(v) |
| |
+--------------------------------+
# Normal to Observable back to Normal
Some libraries also provide ways to create observables from Normal World, subscribe to those observables, then connect emitted values back to Normal World.
We can create observables somewhere outside of React components and somehow pass them in, but most likely we would like to create observables inside React components.
There are two ways to achieve that:
- Event function callbacks. Every time the callback is called, a value is emitted from a Subject.
- Hook dependencies. Changes of props, states or context will trigger component re-rendering. Hooks like
useEffect
can be used to collect changes.
# Function Callbacks
In observable-hooks useObservableState
can also be used for function callbacks.
+--------------------------------+
| Observable World |
+--------------------------------+
| |
| const transform = |
| input$ => input$.pipe(...) |
| |
+-------------^----------+-------+
| |
| |
| |
v-------------------+
const [output, onInput] = useObservableState(
| ^ transform,
| | initialOutput
| | )
| |
| |
| |
+----v--------+------------------+
| Normal World |
+--------------------------------+
| |
| <button onClick={onInput}> |
| {output} |
| </button> |
| |
+--------------------------------+
# Hook dependencies
This is a little different in observable-hooks which does not provide a "Normal-Observable-Normal" way with hook dependencies.
If you read back on what we have just discussed, you should notice that we always end up in the Normal World. But observable-hooks truly shines with its ability to end in the Observable World. Keep reading and I will show you what it means.
# Normal to Observable
# Hook dependencies
In observable-hooks you can use useObservable
or useLayoutObservable
to create observables with hook dependencies.
+--------------------------------+
| Observable World |
+--------------------------------+
| |
| const transform = |
| inputs$ => inputs$.pipe(...) |
| |
| output$ |
+------^-------------------------+
|
|
|
+--------------+
const output$ = useObservable(
transform,
[props.A, state, ctx]
) ^
|
|
|
+---------------------+----------+
| Normal World |
+--------------------------------+
| |
| const App(props) { |
| const [state] = useState() |
| const ctx = useContext(Ctx) |
| } |
| |
+--------------------------------+
# Function Callbacks
You can also use useObservableCallback
to create observables from function callbacks.
+--------------------------------+
| Observable World |
+--------------------------------+
| |
| const transform = |
| input$ => input$.pipe(...) |
| |
| output$ |
+--------------^-----------------+
|
+-----+ |
| v +
const [onInput, output$] = useObservableCallback(
^ transform
| )
|
|
|
|
+---+----------------------------+
| Normal World |
+--------------------------------+
| |
| <button onClick={onInput}> |
| Click |
| </button> |
| |
+--------------------------------+
The resulted observables can then be consumed by Observable to Normal with useObservableState
or useSubscription
.
# Ref to Observable
You can also use useObservableRef
to create observables from ref value.
+--------------------------------+
| Observable World |
+--------------------------------+
| |
| value$ |
+-----------^--------------------+
|
+----+ |
| v +
const [ref, value$] = useObservableRef(
^ initialValue
| )
|
|
|
|
+---+----------------------------+
| Normal World |
+--------------------------------+
| |
| <button ref={ref}> |
| Click |
| </button> |
| |
| // or |
| ref.current = xxx |
+--------------------------------+
The resulted observables can then be consumed by Observable to Normal with useObservableState
or useSubscription
.
# Observable to Observable
Finally, you can also operate on multiple observables. This flexibility is powerful and can greatly simplify the observable flow design.
+--------------------------------+
| Observable World |
+--------------------------------+
| |
| fromProps$ |
| |
| fromState$ |
| |
| fromGlobal$ |
| |
| output$ |
+-----^--------------+-----------+
| |
| |
+--------------v
const output$ = useObservable(
() => combineLatest([
fromProps$,
fromState$,
fromGlobal$
])
)
+--------------------------------+
| |
| Normal World |
| |
+--------------------------------+
The resulted observables can then be consumed by Observable to Normal with useObservableState
or useSubscription
.
# Helpers
There are also sugars like useObservableGetState
and useObservablePickState
which are inspired by lodash get
and pick
.
The Epic (opens new window)-like signature makes the observable transformation logic highly reusable. In fact observable-hooks offers some helpers for common cases to reduce garbage collection burden.