# 核心概念

# 两个世界

要理解 observable-hooks 的设计你需要有两个“世界”的概念:响应式世界与普通世界。


  +--------------------------------+
  |                             |
  |            响应式世界            |
  |                             |
  +--------------------------------+

         +------------------+
         | observable-hooks |
         +------------------+

  +--------------------------------+
  |                             |
  |             普通世界            |
  |                             |
  +--------------------------------+

这两个世界仅是概念上的区分。响应式世界是指 Observable 存放的地方。这可以是在 React 组件里面,但也可以在外部。普通世界是指非响应式世界的地方。

# 响应式世界到普通世界

几乎所有实现 RxJS 与 React 转接的库都会提供相应接口来连接 Observable 值与 React 状态(state)。

# Observable 到 React 状态

在 observable-hooks 中我们可以用 useObservableStateuseObservableEagerState


  +--------------------------------+
  |            响应式世界            |
  +--------------------------------+
  |                                |
  |             input$             |
  |                                |
  +--------------------+-----------+
                       |
                       |
                       |
           v-----------+
  const output = useObservableState(
           |       input$,
           |       initialOutput
           |     )
           |
           |
           |
           |
  +--------v-----------------------+
  |             普通世界            |
  +--------------------------------+
  |                                |
  |         <p>{output}</p>        |
  |                                |
  +--------------------------------+

# Observable 到订阅回调

除了转换到状态,我们还可以提供订阅回调函数通过 useSubscription 订阅 Observables。见 API 文档 了解为什么推荐用它而不是手动 useEffect


  +--------------------------------+
  |             响应式世界           |
  +--------------------------------+
  |                                |
  |             input$             |
  |                                |
  +-------------------+------------+
                      |
                      |
                      |
                      v
    useSubscription(input$, onNext)
                              |
                              |
                              |
                              |
  +---------------------------v----+
  |             普通世界            |
  +--------------------------------+
  |                                |
  |   const onNext = v => log(v)   |
  |                                |
  +--------------------------------+

# 普通世界到响应式世界到普通世界

有些库还提供了方法从普通世界中创建 Observables,然后再订阅这些 Observables,将值导回普通世界。

我们可以在 React 组件以外的某个地方创建 Observables 然后通过各种方式传进组件使用。但更多情况是我们希望可以在 React 组件里面创建 Observables。

有两种方式可以做到:

  1. 事件回调函数。每当函数被调用,新的值从 Subject 中弹出。
  2. Hook 依赖。React 的 props、states 和 context 的变化都会触发组件渲染。如 useEffect 之类的 hooks 可以收集到这些变化。

# 事件回调函数

在 observable-hooks 中 useObservableState 还支持接收事件回调函数。


     +--------------------------------+
     |             响应式世界           |
     +--------------------------------+
     |                                |
     |  const transform =             |
     |    input$ => input$.pipe(...)  |
     |                                |
     +-------------^----------+-------+
                   |          |
                   |          |
                   |          |
          v-------------------+
 const [output, onInput] = useObservableState(
          |        ^         transform,
          |        |         initialOutput
          |        |       )
          |        |
          |        |
          |        |
     +----v--------+------------------+
     |             普通世界            |
     +--------------------------------+
     |                                |
     |   <button onClick={onInput}>   |
     |    {output}                    |
     |   </button>                    |
     |                                |
     +--------------------------------+

# Hook 依赖

在 observable-hooks 中 Hook 依赖的处理与其它库有点不太一样,我们并没有针对 hook 依赖提供“普通世界-响应式世界-普通世界”的方式。

如果你重新看回我们刚讨论过的接口,也许能注意到我们最终都是以普通世界结束。但 observable-hooks 真正发光的地方在于它能够让我们结束在响应式世界。具体什么意思请容我细说。

# 普通世界到响应式世界

# Hook 依赖

在 observable-hooks 中你可以通过 useObservableuseLayoutObservable 利用 hook 依赖创建 Observable。


   +--------------------------------+
   |            响应式世界            |
   +--------------------------------+
   |                                |
   | const transform =              |
   |   inputs$ => inputs$.pipe(...) |
   |                                |
   |   output$                      |
   +------^-------------------------+
          |
          |
          |
          +--------------+
 const output$ = useObservable(
                   transform,
                   [props.A, state, ctx]
                 )       ^
                         |
                         |
                         |
   +---------------------+----------+
   |             普通世界            |
   +--------------------------------+
   |                                |
   | const App(props) {             |
   |   const [state] = useState()   |
   |   const ctx = useContext(Ctx)  |
   | }                              |
   |                                |
   +--------------------------------+

# 事件回调函数

你还可以通过 useObservableCallback 利用事件回调函数来创建 Observable。


        +--------------------------------+
        |            响应式世界            |
        +--------------------------------+
        |                                |
        |  const transform =             |
        |    input$ => input$.pipe(...)  |
        |                                |
        |           output$              |
        +--------------^-----------------+
                       |
            +-----+    |
            |     v    +
 const [onInput, output$] = useObservableCallback(
            ^                 transform
            |               )
            |
            |
            |
            |
        +---+----------------------------+
        |             普通世界            |
        +--------------------------------+
        |                                |
        |   <button onClick={onInput}>   |
        |    Click                       |
        |   </button>                    |
        |                                |
        +--------------------------------+

得到的 Observable 可以借助 响应式世界到普通世界 模式,通过 useObservableStateuseSubscription 接收。

# Ref 生成 Observable

你还可以通过 useObservableRef 利用 React ref.current 的改变产生值。


        +--------------------------------+
        |            响应式世界            |
        +--------------------------------+
        |                                |
        |         value$                 |
        +-----------^--------------------+
                    |
            +----+  |
            |    v  +
    const [ref, value$] = useObservableRef(
            ^               initialValue
            |             )
            |
            |
            |
            |
        +---+----------------------------+
        |             普通世界            |
        +--------------------------------+
        |                                |
        |   <button ref={ref}>           |
        |    Click                       |
        |   </button>                    |
        |                                |
        |   // or                        |
        |   ref.current = xxx            |
        +--------------------------------+

得到的 Observable 可以借助 响应式世界到普通世界 模式,通过 useObservableStateuseSubscription 接收。

# 响应式世界到响应式世界

最后,我们还可以操作任意多条 Observable。这提供了巨大的灵活性,可以简化许多流的设计。


    +--------------------------------+
    |            响应式世界            |
    +--------------------------------+
    |                                |
    |           fromProps$           |
    |                                |
    |           fromState$           |
    |                                |
    |           fromGlobal$          |
    |                                |
    |  output$                       |
    +-----^--------------+-----------+
          |              |
          |              |
          +--------------v
  const output$ = useObservable(
                    () => combineLatest([
                      fromProps$,
                      fromState$,
                      fromGlobal$
                    ])
                  )

    +--------------------------------+
    |                             |
    |             普通世界            |
    |                             |
    +--------------------------------+

得到的 Observable 可以借助 响应式世界到普通世界 模式,通过 useObservableStateuseSubscription 接收。

# 辅助方法

Osbservable-hooks 中类 Epic (opens new window) 的设计可以让 Observable 的转换逻辑高度可复用。事实上 observable-hooks 已经提供了一些常用 辅助方法 来减少垃圾回收压力。