admin管理员组

文章数量:1356560

In Angular, I usually subscribe to a client providing me with an observable of a GET call as Google dictates.

httpClient.get<Data>("http://blah")
  .subscribe(
    result => { ... },
    error => { ... },
    () => { ... }
  );

I'm trying to learn React and perform corresponding operation. The info I find is about using promises, which I prefer not to go with. Of course, the services of Angular lack their counterpart in React as such but since rxjs is an independent library, I feel that it makes sense to try to incorporate it into a React project.

How do I do that and what am I missing in my googling looking for it?

I'm also humbly considering the alternative that, since React is based on a different paradigm (i.e. stores instead of services), I might be barking up a very awkward tree.

In Angular, I usually subscribe to a client providing me with an observable of a GET call as Google dictates.

httpClient.get<Data>("http://blah")
  .subscribe(
    result => { ... },
    error => { ... },
    () => { ... }
  );

I'm trying to learn React and perform corresponding operation. The info I find is about using promises, which I prefer not to go with. Of course, the services of Angular lack their counterpart in React as such but since rxjs is an independent library, I feel that it makes sense to try to incorporate it into a React project.

How do I do that and what am I missing in my googling looking for it?

I'm also humbly considering the alternative that, since React is based on a different paradigm (i.e. stores instead of services), I might be barking up a very awkward tree.

Share Improve this question asked Sep 22, 2018 at 12:27 DonkeyBananaDonkeyBanana 3,5765 gold badges33 silver badges68 bronze badges 3
  • Do you have some specific motivation to not use promises? There are not so many cases where you can really benefit from using Http observable in Angular (basically the cases are piping, cancellation and retry). I find myself doing toPromise() in 90% cases because it plays nicely with async..await. In React, there are much less benefits, unless you're already heavily using observables throughout the app. – Estus Flask Commented Sep 22, 2018 at 15:04
  • @estus I'll be emitting data over an extended period of time shooting in values as they bee available. – DonkeyBanana Commented Sep 23, 2018 at 14:05
  • The reason why there's not much information about how RxJS is used with React is that React doesn't have observables that could be used as sources. So the thing you're doing isn't framework specific. You can use promises with mergeMap as well, btw. Extracting Http from Angular and using it in React would be not a bad idea as well. – Estus Flask Commented Sep 23, 2018 at 14:14
Add a ment  | 

3 Answers 3

Reset to default 4

The reason why Angular Http benefits from observables is that some parts of Angular use observables, so they can be efficiently posed, e.g. reactive form observable can be throttled and piped to Http. It's not unmon that Http observable is converted toPromise(), just because it's plete observable with single value and it can benefit from async..await when being a promise.

Unless React project heavily uses observables (for instance, with redux-observable), there will be much less benefits than in Angular.

As another answer mentions, there's built-in HTTP request API in RxJS, rxjs/ajax. It's a wrapper around XMLHttpRequest, this means that it provides cancellable requests, as opposed to some promise-based APIs, notably Fetch. It's very simplistic and lacks features that may be expected from Http alternative - interceptors, etc.

Axios can be generally remended as framework-agnostic, full-featured promise-based alternative to Angular Http. It was modeled after AngularJS $http. A promise from it can be converted to an observable but additional measures should be taken to make a request cancellable.

TL;DR: the biggest selling of Http is that its observables can be posed with other observables. If there are none, the benefits are much less obvious. Promises can benefit from async..await syntactic sugar while observables have to be converted to promises any way to benefit from it.

Just instead of Angular's httpClient you can use build-in RxJS ajax methods:

import { ajax } from 'rxjs/ajax';

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

Live demo: https://stackblitz./edit/rxjs6-demo-nsgdri?file=index.ts

Please take a look on a next example:

This works when you want to cancel AJAX request on a ponent destroy or debounce AJAX on the input values are changed.

Solution is provided using React Hooks and 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 ponent)
    return new Subject<string>();
  });

  useEffect(() => {
    // Effect that will be initialized once on a react ponent 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;

NOTE: Use switchMap to start AJAX call. In this case you will subscribe once and have the same results.

本文标签: