在组件中订阅数据流

在jorum中数据流是Observable而非裸的数据,因此我们需要通过一定的方式对数据进行订阅。

useStream

在函数组件中,可以使用useStream来订阅数据流:

const App = function(props) {
  const fooBloc = useBloc(FooBloc)
  const data = useStream(fooBloc.data$)
  return (
    <div>
      {data}
    </div>
  )
}

当data更新时,App组件也会自动重新渲染。

useStream本质上就是useEffectuseState,订阅数据流并在其更新的时候自动setState

suspense

在上面的例子中,其实存在一个小问题,data可能并没有被初始化:

const App = function(props) {
  const fooBloc = useBloc(FooBloc)
  const data = useStream(fooBloc.data$)
  console.log(data)
  //...
}

//first time: undefined
//second time: "some value"

即便fooBloc.data$BehaviorSubjectReplaySubjectdata变量在第一次渲染的时候都会是undefined,除非我们给它指定了一个初始值:

const data = useStream(fooBloc.data$, 'some initial value')
console.log(data)

//first time: "some initial value"
//second time: "some value"

为了解决这个问题,我们需要使用一个组件生成器:suspense函数。

const App = suspense(function App(props) {
  const fooBloc = useBloc(FooBloc)
  const data = useStream(fooBloc.data$)
  return () => {
    return (
      <div>
        {data}
      </div>
    )
  }
})

不难发现,suspense的那个参数(不妨记为函数A)是一个高阶函数,它会返回另一个函数(不妨记为函数B)。通过这种方式,我们把一个函数组件切分成了两个部分,第一部分(函数A)是useBlocuseStream,第二部分(函数B)是其余的逻辑。suspense会先对函数A中使用到的stream进行检查,如果这些useStream都被初始化了,那么才会执行函数B渲染组件,反之则会直接返回null。因此,我们无需手动处理dataundefined的情况。

如果在开发中发现你的suspense组件显示不出来,请先确保:在组件被创建后,其中所有useStream订阅的流都发送过值。

由于suspense是一个组件生成器而非高阶组件,因此,如果有其他的组件wrapper函数,suspense必须写在最内侧

// √
const App = withRouter(memo(suspense(function App(props) {
    //...
})))

// ×
const App = suspense(withRouter(memo(function App(props) {
    //...
})))

useSubscription

有的时候,我们希望把数据流作为事件通知器,或者是希望自定义一些数据更新时的处理逻辑,这时可以使用useSubscription来订阅事件。

const App = function() {
  const fooBloc = useBloc(FooBloc)
  useSubscription(fooBloc.foo$, () => {
    // do something here
  })
  return (
    //...
  )
}

useSubscription不需要和suspense配合使用。

Subscribe

在普通类组件中,可以直接使用Subscribe组件:

<Subscribe to={fooBloc.data$}>
  {data => (
    <div>
      {data}
    </div>
  )}
</Subscribe>

Last updated