如何在 React 中正确地进行 GET 调用,返回一个 observable(类似于 Angular 中的方法而不使用 Promise)?
Posted
技术标签:
【中文标题】如何在 React 中正确地进行 GET 调用,返回一个 observable(类似于 Angular 中的方法而不使用 Promise)?【英文标题】:How to properly make a GET call in React returning an observable (resembling the method in Angular and not using promises)? 【发布时间】:2019-02-26 15:02:03 【问题描述】:在 Angular 中,我通常订阅一个为我提供可观察到的 GET 调用 as Google dictates 的客户端。
httpClient.get<Data>("http://blah")
.subscribe(
result => ... ,
error => ... ,
() => ...
);
我正在尝试学习 React 并执行相应的操作。我找到的信息是关于使用 Promise 的,我不想使用。当然,Angular 的服务在 React 中缺乏对应的服务,但由于 rxjs 是一个独立的库,我觉得尝试将其合并到 React 项目中是有意义的。
我该怎么做?我在谷歌搜索时遗漏了什么?
我也在谦虚地考虑替代方案,因为 React 是基于不同的范例(即商店而不是服务),我可能会在一个非常尴尬的树上吠叫。
【问题讨论】:
你有什么不使用承诺的具体动机吗?在 Angular 中使用 Http observable 可以真正受益的情况并不多(基本上这些情况是管道、取消和重试)。我发现自己在 90% 的情况下都在使用 toPromise(),因为它与async..await
配合得很好。在 React 中,好处要少得多,除非您已经在整个应用程序中大量使用 observables。
@estus 我将在很长一段时间内以可用的值发送数据。
没有太多关于 RxJS 如何与 React 一起使用的信息的原因是 React 没有可用作源的可观察对象。所以你正在做的事情不是特定于框架的。顺便说一句,您也可以将 Promise 与 mergeMap
一起使用。从 Angular 中提取 Http
并在 React 中使用它也不错。
【参考方案1】:
您可以使用内置的 RxJS ajax 方法来代替 Angular 的 httpClient
:
import ajax from 'rxjs/ajax';
ajax.get('https://httpbin.org/get')
.subscribe(console.log);
现场演示:https://stackblitz.com/edit/rxjs6-demo-nsgdri?file=index.ts
【讨论】:
【参考方案2】:Angular Http
受益于 observables 的原因是 Angular 的某些部分使用 observables,因此可以有效地组合它们,例如可观察的反应形式可以被限制并通过管道传输到Http
。 Http
observable 被转换为 toPromise()
并不少见,因为它是完全可观察的,具有单个值,并且当它是一个 Promise 时,它可以从 async..await
中受益。
除非 React 项目大量使用 observables(例如,使用 redux-observable
),否则它所带来的好处将远少于 Angular。
正如另一个答案提到的,RxJS 中有内置的 HTTP 请求 API,rxjs/ajax
。它是XMLHttpRequest
的包装器,这意味着它提供可取消的请求,而不是一些基于promise 的API,尤其是Fetch。它非常简单,缺乏Http
替代方案可能预期的功能 - 拦截器等。
通常可以推荐 Axios 作为 Angular Http
的与框架无关、基于 Promise 的全功能替代方案。它以 AngularJS $http
为蓝本。来自它的承诺可以转换为可观察的,但应采取额外措施使请求可取消。
TL;DR:Http
的最大卖点是它的 observables 可以与其他 observables 组合。如果没有,好处就不那么明显了。 Promise 可以从 async..await
语法糖中受益,而 observables 必须以任何方式转换为 Promise 才能从中受益。
【讨论】:
【参考方案3】:请看下一个例子:
当您想取消对组件销毁的 AJAX 请求或在输入值更改时反跳 AJAX 时,此方法有效。
使用 React Hooks 和 RxJS 提供解决方案:
import React, useEffect, useState from "react";
import ajax from "rxjs/ajax";
import debounceTime, delay, takeUntil from "rxjs/operators";
import Subject from "rxjs/internal/Subject";
const App = () =>
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(true);
const [filterChangedSubject] = useState(() =>
// Arrow function is used to init Singleton Subject. (in a scope of a current component)
return new Subject<string>();
);
useEffect(() =>
// Effect that will be initialized once on a react component init.
const subscription = filterChangedSubject
.pipe(debounceTime(200))
.subscribe((filter) =>
if (!filter)
setLoading(false);
setItems([]);
return;
ajax(`https://swapi.dev/api/people?search=$filter`)
.pipe(
// current running ajax is canceled on filter change.
takeUntil(filterChangedSubject)
)
.subscribe(
(results) =>
// Set items will cause render:
setItems(results.response.results);
,
() =>
setLoading(false);
,
() =>
setLoading(false);
);
);
return () =>
// On Component destroy. notify takeUntil to unsubscribe from current running ajax request
filterChangedSubject.next("");
// unsubscribe filter change listener
subscription.unsubscribe();
;
, []);
const onFilterChange = (e) =>
// Notify subject about the filter change
filterChangedSubject.next(e.target.value);
;
return (
<div>
Cards
loading && <div>Loading...</div>
<input onChange=onFilterChange></input>
items && items.map((item, index) => <div key=index>item.name</div>)
</div>
);
;
export default App;
注意:使用 switchMap 启动 AJAX 调用。在这种情况下,您将订阅一次并获得相同的结果。
【讨论】:
我不喜欢亲自订阅 @Paulquappe 你是对的,这个代码部分可以重新实现为一个只有一个订阅的流程。以上是关于如何在 React 中正确地进行 GET 调用,返回一个 observable(类似于 Angular 中的方法而不使用 Promise)?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 React Hooks 和 Context API 正确地将来自 useEffect 内部调用的多个端点的数据添加到状态对象?
我正在开发代码(例如Django,Node,甚至是前端React),如何根据需要正确地将其正确连续地部署到服务器上