React:如何使用 RxJS 优化数据流。
现在我们熟悉使用它 functional component 和 hooks 来处理 react 逻辑。熟悉 Angular 用户可能更熟悉 RxJS 处理异步和数据状态。让我们看看。 React 中使用 RxJS 会有什么优势吗?
一般来说,处理组件中的数据流只有三种情况:
-
Component 中 props 的变化
-
用户在界面上操作,触发 event 修改 component 的数据
-
Component 中监听的 store 触发数据变化
让我们看一个非常简单的例子:
有一个 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 合为一体。
那么,问题是,使用数据流来处理数据有什么好处呢?
-
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