如何在 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,因此可以有效地组合它们,例如可观察的反应形式可以被限制并通过管道传输到HttpHttp 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 内部调用的多个端点的数据添加到状态对象?

Vue/React:调用函数传递数据的正确方法?

在 react js 中进行 API 调用的正确方法是啥?

我正在开发代码(例如Django,Node,甚至是前端React),如何根据需要正确地将其正确连续地部署到服务器上

如何使用 thunk 在 react-redux 挂钩中进行异步调用?

在 MERN 应用程序(React 客户端)中外包 API 调用的正确方法?