React 구성 요소에서 비동기식/대기 작업을 올바르게 처리

19392 단어

배경.


최근 추문에 따르면 async/await는 처리 방식이 어느 정도 복잡성이 없으면 React 구성 요소와 잘 어울리지 않는다고 한다.

단 아브라모프

구성 요소의 비동기식/대기 오류가 발생했습니다.구성 요소는 시간에 따라 변화하는 아이템/상태를 가지고 있습니다.async/await 모델은 기다릴 때 변화를 처리하는 방법을 제공하지 않습니다.따라서 코드는 간단하고 결함이 있거나 복잡하고 이해하기 어렵다.twitter.com/cramforce/stat…
2020년 4월 4일 오후 13:39
마르테우블
# 클램포스
넌 내 차가운 죽음의 손에서 async Wait를 가져갔어https://t.co/xlmqjAowqt

왜 이렇게 복잡해?


React에서나 대부분의 다른 UI 라이브러리/프레임워크에서 비동기 코드를 처리하는 것은 복잡합니다.언제든지 비동기 코드가 완성되기를 기다리고 있기 때문에, 구성 요소 도구가 업데이트되거나, 구성 요소가 해제될 수도 있습니다.

브래드 리틀

Async/await는 노드 웹 응용 프로그램에서 매우 좋습니다. 요청-응답 생명주기가 있고 현재 요청에 영향을 주는 새로운 요청을 받지 않습니다.전단 부품은 그런 사치스럽지 않다!그들은 의외로 새로운 아이템을 하역 또는 수령할 수 있습니다!twitter.com/dan_abramov/st…
2020년 4월 4일 오후 14:03
단 아브라모프
구성 요소의 비동기식/대기 오류가 발생했습니다.구성 요소는 시간에 따라 변화하는 아이템/상태를 가지고 있습니다.async/await 모델은 기다릴 때 변화를 처리하는 방법을 제공하지 않습니다.따라서 코드는 간단하고 결함이 있거나 복잡하고 이해하기 어렵다.https://t.co/FvRmw8TBCE

문제를 끄집어내다


첫 번째 트윗에서 말했듯이 복잡하지만 여기서 무슨 일이 일어났는지 설명해 보겠습니다.
다음 코드 세션에서는 axios 라이브러리를 사용하여 비동기식 HTTP 요청을 보내는 구성 요소를 볼 수 있습니다.
import React, { useState, useEffect } from "react";
import axios from "axios";

export default function RandomJoke({ more, loadMore }) {
  const [joke, setJoke] = useState("");

  useEffect(() => {
    async function fetchJoke() {
      try {
        const asyncResponse = await axios("https://api.icndb.com/jokes/random");
        const { value } = asyncResponse.data;
        setJoke(value.joke);
      } catch (err) {
        console.error(err);
      }
    }

    fetchJoke();
  }, [more]);

  return (
    <div>
      <h1>Here's a random joke for you</h1>
      <h2>{`"${joke}"`}</h2>
      <button onClick={loadMore}>More...</button>
    </div>
  );
}
좋아요.상술한 구성 요소에는 어떤 문제가 있습니까?
1) 비동기 요청이 완료되기 전에 구성 요소를 마운트 해제하면 비동기 요청이 실행되고 완료될 때 setState 함수를 호출하여 React 경고가 발생합니다😕:

2) 비동기 요청이 완료되기 전에 "more"속성을 변경하면 이 효과는 다시 실행되므로 비동기 함수를 다시 호출합니다.첫 번째 요청이 두 번째 요청 이후에 완료되면 경쟁 조건을 초래할 수 있다.

이것은 잘못된 것일 수도 있습니다. 왜냐하면 우리는 우리가 요청한 최신 비동기 호출 결과를 얻기를 희망하기 때문입니다.
분명히, 이렇게 간단한 프로그램에서는 가능하지만, 텍스트 검색 API를 기반으로 하는 프로그램이 있다면, 입력한 최신 검색 결과를 항상 보여 주기를 원한다.

어떻게 복원합니까


문제 1 - Ref를 사용하여 React 경고를 수정합니다.
import React, { useState, useEffect, useRef } from "react";
import axios from "axios";

export default function RandomJoke({ more, loadMore }) {
  const [joke, setJoke] = useState("");
  const componentIsMounted = useRef(true);

  useEffect(() => {
    // each useEffect can return a cleanup function
    return () => {
      componentIsMounted.current = false;
    };
  }, []); // no extra deps => the cleanup function run this on component unmount

  useEffect(() => {
    async function fetchJoke() {
      try {
        const asyncResponse = await axios("https://api.icndb.com/jokes/random");
        const { value } = asyncResponse.data;

        if (componentIsMounted.current) {
          setJoke(value.joke);
        }
      } catch (err) {
        console.error(err);
      }
    }

    fetchJoke();
  }, [more]);

  return (
    <div>
      <h1>Here's a random joke for you</h1>
      <h2>{`"${joke}"`}</h2>
      <button onClick={loadMore}>More...</button>
    </div>
  );
}
보시다시피, 위에서 하는 일은 구성 요소를 마운트 해제할 때만 업데이트하는 ref component Is Mounted를 추가하는 것입니다.이를 위해, 우리는 청소 기능이 있는 추가 효과를 추가했다.그리고 상태를 설정하기 전에 데이터를 얻는 곳에서 구성 요소가 설치되어 있는지 확인합니다.문제가 해결되었다✅! 이제 복구를 시작하겠습니다.
문제2: 실제 비동기식 문제를 해결합니다.우리가 원하는 것은, 만약 우리가 비동기적인 작업이 발생하도록 요구한다면, 그것을 취소하고, 누군가가 그것을 완성하지 못하도록 하는 방법이 필요하다는 것이다.다행히도, axios는 바로 우리가 필요로 하는 취소 영패이다💥
import React, { useState, useEffect, useRef } from "react";
import axios, { CancelToken } from "axios";

export default function RandomJoke({ more, loadMore }) {
  const [joke, setJoke] = useState("");
  const componentIsMounted = useRef(true);

  useEffect(() => {
    // each useEffect can return a cleanup function
    return () => {
      componentIsMounted.current = false;
    };
  }, []); // no extra deps => the cleanup function run this on component unmount

  useEffect(() => {
    const cancelTokenSource = CancelToken.source();

    async function fetchJoke() {
      try {
        const asyncResponse = await axios(
          "https://api.icndb.com/jokes/random",
          {
            cancelToken: cancelTokenSource.token,
          }
        );
        const { value } = asyncResponse.data;

        if (componentIsMounted.current) {
          setJoke(value.joke);
        }
      } catch (err) {
        if (axios.isCancel(err)) {
          return console.info(err);
        }

        console.error(err);
      }
    }

    fetchJoke();

    return () => {
      // here we cancel preveous http request that did not complete yet
      cancelTokenSource.cancel(
        "Cancelling previous http call because a new one was made ;-)"
      );
    };
  }, [more]);

  return (
    <div>
      <h1>Here's a random joke for you</h1>
      <h2>{`"${joke}"`}</h2>
      <button onClick={loadMore}>More...</button>
    </div>
  );
}
여기서 무슨 일이 일어났는지:
1) 비동기 데이터의 효과를 가져올 때마다 취소 영패원을 만들어서 axios에 전달합니다.
2) 비동기 작업이 완료되기 전에 효과를 다시 호출하면 React의useEffect 정리 기능을 사용합니다.정리는 효과를 다시 호출하기 전에 실행되기 때문에 cancelTokenSource를 호출해서 취소할 수 있습니다.취소().

결론


따라서 React에서 비동기식 작업을 처리하는 것은 좀 복잡하다.물론, 우리는 사용자 정의 갈고리를 사용하여 데이터를 얻어서 그것을 추상화할 수 있다.
모든 상황에서, 너는 항상 이런 문제들을 걱정할 필요가 없을 것이다.구성 요소가 잘 격리되어 있다면, 이것은 실행 중인 비동기 코드의 prop 값에 의존하지 않는다는 것을 의미합니다. 그러면 모든 것이 정상적이어야 합니다.구성 요소가 자주 마운트 해제되면 이 문제를 해결해야 할 수도 있습니다.

좋은 웹페이지 즐겨찾기