51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

React:如何使用 RxJS 去优化数据流

React:如何使用 RxJS 去优化数据流。

现在我们比较熟悉的是使用 functional component 和 hooks 来处理 react 逻辑。熟悉 Angular 的用户可能比较熟悉通过 RxJS 来处理异步和数据状态。那么我们看一下 React 中使用 RxJS 会不会有什么优势呢?

{#_label1}

一般来说,处理组件中的数据流无非三种情况: {#heading-1}

  • Component 中 props 的变化

  • 用户在界面上操作,触发 event 修改 component 的数据

  • Component 中监听的 store 触发数据变化

{#_lab2_1_0}

下面我们看一个很简单的例子: {#heading-2}

image.png

有一个 component,显示 input 中输入的数据。逻辑也很简单:

export function ShowInput(props: { data: string; }): JSX.Element {
  return <>{props.data}</>;
}

也就是第一种情况,component 的状态是通过,props 传递进来的。

这当然没有问题,当数据变化的时候重新渲染即可。假设我们每一次输入就会触发一次 api call,然后把 api 返回的内容显示在界面上。这里就需要处理两个问题:

  • 尽可能减少 api call 的次数(比如用户输入有 500ms 的停顿以上时,我们认为用户已经输入结束,才开始 call api)

  • 用户如果多次输入,会有多次 api call,那么后面 api 返回的数据应该永远覆盖前面 api 返回的数据。

  • 暂时不考虑 api 可能出错,需要重新 call api 的情况

熟悉 RxJS 的话,会知道,这是一个非常简单的数据流的问题,如果我们把 props 的数据看作是一个数据流的话,就可以非常简单的解决了。

const apiRespons$ = data$.pipe(
  debounceTime(500),
  switchMap(data => getApiResponse(data)),
);
// Mock API call
function getApiResponse(data: string) {
  return of(data + ' data from api').pipe(delay(1000));
}

那么,问题就来了,如何将 props data 转换成 stream, 又如何将处理完的 stream 转换成 component state 呢。

这里就需要引入一个库: rxjs-hooks

用法一: 将 stream 转换为 componet state.

比如我们已经得到了 apiResponse$,如何在 component 中显示的呢?

const apiRespons$ = data$.pipe(
  debounceTime(500),
  switchMap(data => getApiResponse(data)),
);
const apiResponse = useObservable(() => apiRespons$);

那么如何结合 props 的变化使用呢?

用法二: 将 props 转换为 stream useObservable 还可以输入两个参数:

  • 状态的初始值 (类似,useState)

  • 监听变量(类似 useEffect 的数组参数),转换为 stream 作为函数的参数

完整的代码会变成这样:

export function ShowInput(props: { data: string; }): JSX.Element {
  const apiResponse = useObservable((_, input$) => input$.pipe(
    debounceTime(500),
    switchMap(([data]) => getApiResponse(data)),
  ), '', [props.data]);
  return <>{apiResponse}</>;
}

当然,也有可能,数据的变化时当前 component 的,不是 props 传进来的。也就是说这个 input 可能会是在当前 component 中。

当然,我们简单的把 input 放在当前的 componnet 中,把监听内容从 props 换成 state. 也比较简单。

const [data, setData] = useState<string>();
const apiResponse = useObservable((_, input$) => input$.pipe(
  debounceTime(500),
  switchMap(([inputData]) => getApiResponse(inputData || '')),
), '', [data]);

那么,有没有更简单一点的方法呢?

就涉及到用法三:将 event 函数的调用自动转换为 stream

const [onInputChange, apiResponse] = useEventCallback((data$: Observable<string>) => data$.pipe(
  debounceTime(500),
  switchMap((data) => getApiResponse(data || '')),
), '');

那么,有没有更简单一点的方法呢?

就涉及到用法三:将 event 函数的调用自动转换为 stream

const [onInputChange, apiResponse] = useEventCallback((data$: Observable<string>) => data$.pipe(
  debounceTime(500),
  switchMap((data) => getApiResponse(data || '')),
), '');

这就将 useState 和 useObservable 合为一体。第一个参数时 event 函数,第二个参数时 state。

当然,如果有使用 redux observable 的话,就可以很好的跟 rxjs-hooks 合为一体。

{#_lab2_1_1}

那么,问题来了,使用数据流的方式来处理数据有什么好处呢? {#heading-3}

  • RxJS 内置了很多数据流的处理方式,可以大大的简化我们处理数据的流程。类似于异步中的 lodash.

  • 在 React 中我们往往会同时使用 Redux 和 Hooks, 某种程度上说,我们会使用 Redux 处理全局或者说大的状态管理,hooks 处理 component 层,或者说小的数据状态管理。使用 redux observable 和 rxjs hooks 不失为一种沟通全局状态和局部状态的好的方式。

  • 我们直到 redux 时跨平台的。但是有些时候我们并不希望把所有的数据状态都封装在 redux 中。如果要实现数据状态的跨平台,而不使用 redux 的话,rxjs 实现一个状态关机工具无非是最简单的。因为一个 subject 就是一个最简单的 store. 让我们的主要逻辑不依赖于框架,是最好的多平台共享的方式。

对于 component 层的局部状态,逻辑共享:

react   angular  
|       /
hooks  component store
|      /
js rxjs store

对于 redux 状态共享

react     angular
|         /
redux observable

赞(0)
未经允许不得转载:工具盒子 » React:如何使用 RxJS 去优化数据流