Javascript 如何正确地在React中调用GET,返回一个可观察的(类似于Angular中的方法,不使用承诺)?

Javascript 如何正确地在React中调用GET,返回一个可观察的(类似于Angular中的方法,不使用承诺)?,javascript,reactjs,typescript,rxjs,observable,Javascript,Reactjs,Typescript,Rxjs,Observable,在Angular中,我通常订阅一个客户机,该客户机为我提供一个GET调用的可观察性 httpClient.get(“http://blah") .订阅( 结果=>{…}, 错误=>{…}, () => { ... } ); 我正在尝试学习如何反应并执行相应的操作。我发现的信息是关于使用承诺的,我不喜欢这样。当然,Angular的服务在React中缺少对应的服务,但由于rxjs是一个独立的库,我觉得尝试将其合并到React项目中是有意义的 我如何做到这一点?我在谷歌搜索中遗漏了什么 我还谦虚地考

在Angular中,我通常订阅一个客户机,该客户机为我提供一个GET调用的可观察性

httpClient.get(“http://blah")
.订阅(
结果=>{…},
错误=>{…},
() => { ... }
);
我正在尝试学习如何反应并执行相应的操作。我发现的信息是关于使用承诺的,我不喜欢这样。当然,Angular的服务在React中缺少对应的服务,但由于rxjs是一个独立的库,我觉得尝试将其合并到React项目中是有意义的

我如何做到这一点?我在谷歌搜索中遗漏了什么


我还谦虚地考虑了另一种选择,因为React基于不同的范例(即存储而不是服务),我可能会陷入一个非常尴尬的境地。

只是代替Angular的
httpClient
,您可以使用内置RxJS ajax方法:

import { ajax } from 'rxjs/ajax';

ajax.get('https://httpbin.org/get')
  .subscribe(console.log);

现场演示:

Angular
Http
之所以能从可观测数据中获益,是因为Angular的某些部分使用了可观测数据,因此可以有效地组合它们,例如,反应形式的可观测数据可以被限制并通过管道传输到
Http
Http
observable被转换为
toPromise()
,这并不少见,因为它是完全可观察的,只有一个值,并且可以从
async.中获益。当它是一个承诺时,等待

除非React项目大量使用可观测数据(例如,使用
redux observable
),否则其收益将远远低于Angular项目

另一个答案提到,RxJS中内置了HTTP请求API,
RxJS/ajax
。它是一个围绕
XMLHttpRequest
的包装器,这意味着它提供了可取消的请求,而不是一些基于承诺的api,尤其是Fetch。它非常简单,缺少
Http
alternative-interceptors等所期望的特性

Axios通常被推荐为与框架无关的、功能齐全的基于承诺的替代品,而不是Angular
Http
。它是按照AngularJS
$http
建模的。it部门的承诺可以转化为可观察的承诺,但应采取额外措施使请求可取消


TL;DR:Http
Http
最大的卖点是它的可观测值可以与其他可观测值组合。如果没有,好处就不那么明显了。承诺可以从
async..wait
语法糖中受益,而可观察对象必须以任何方式转换为承诺才能从中受益。

如果要取消组件销毁请求并取消输入更改的ajax,请看下一个示例。 使用React挂钩和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;
import React,{useffect,useState}来自“React”;
从“rxjs/ajax”导入{ajax};
从“rxjs/operators”导入{debounceTime,delay,takeUntil};
从“rxjs/internal/Subject”导入{Subject};
常量应用=()=>{
const[items,setItems]=useState([]);
const[loading,setLoading]=useState(true);
常量[filterChangedSubject]=useState(()=>{
//Arrow函数用于初始化单例主题。(在当前组件的范围内)
返回新主题();
});
useffect(()=>{
//将在react组件init上初始化一次的效果。
const subscription=filterChangedSubject
.管道(去BounceTime(200))
.订阅((筛选器)=>{
如果(!过滤器){
设置加载(假);
集合项目([]);
返回;
}
阿贾克斯(`https://swapi.dev/api/people?search=${filter}`)
.烟斗(
//当前运行的ajax在过滤器更改时被取消。
takeUntil(过滤器更改主题)
)
.订阅(
(结果)=>{
//设置项目将导致渲染:
设置项(结果、响应、结果);
},
() => {
设置加载(假);
},
() => {
设置加载(假);
}
);
});
return()=>{
//在组件销毁时。通知takeUntil取消订阅当前正在运行的ajax请求
filterChangedSubject.next(“”);
//取消订阅筛选器更改侦听器
订阅。取消订阅();
};
}, []);
常量onFilterChange=(e)=>{
//将过滤器更改通知主题
filterChangedSubject.next(如target.value);
};
返回(
卡
{正在加载和正在加载…}
{items&&items.map((item,index)=>{item.name})
);
};
导出默认应用程序;

您是否有不使用承诺的特定动机?在很多情况下,在Angular中使用Http observable并没有真正的好处(基本上是管道、取消和重试)。我发现自己在90%的情况下使用toPromise(),因为它可以很好地处理
async..wait
。在React中,好处要少得多,除非你已经在整个应用程序中大量使用了可观测数据。@estus我将在一段较长的时间内发射数据,并在数据可用时拍摄值。关于RxJS如何与React一起使用,没有太多信息的原因是React没有可作为源使用的可观测数据。因此,您正在做的事情不是特定于框架的。顺便说一句,您也可以将承诺与
mergeMap
一起使用。从Angular中提取
Http
,并在React中使用它也不是一个坏主意。
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;