React useffect의 비동기 처리 및 정리

12096 단어 ReactuseEffecttech
React의useffect에서 청소할 비동기 처리를 할 때 주의해야 할 부분을 예로 들자.
이상한 점과 개선된 점이 있으면 지적해 주세요.

제재


지정한 음원을 재생하는 구성 요소를 만듭니다.음원 지정 후 바로 재생, 지정 변경 후 원래의 소리를 멈추고 새로운 소리를 재생합니다.
설치로 useEffect에서 제작HTMLAudioElement하여 조작합니다.
다음은 코드와 동작을 확인할 수 있습니다.

불완전한 클린업 예


지정된 사운드를 재생성하는 어셈블리의 기본 뼈대는 다음과 같습니다.(Reactv18.0.0, Type Scriptv4.4.2 환경)
Example1.tsx
import { FC, useEffect } from "react";

type Props = { soundSrc: string };

export const Example1: FC<Props> = ({ soundSrc }) => {
  useEffect(() => {
    const audio = new Audio(soundSrc);
    // audio.play()は、ロード完了時にfulfillされるPromiseを返し、再生を開始する
    audio.play().catch((e) => alert(e));

    return () => {
      // ロード完了前にクリーンアップされる場合を考慮していない
      audio.pause();
    };
  }, [soundSrc]);

  return <p>Example1</p>;
};
이 코드는 audio.play() 불러오기 전에 청소를 실행하는 것을 고려하지 않았습니다.
그럼에도 불구하고 마운트는 보통 짧은 시간 안에 이루어지기 때문에 그 정도의 시간 안에 구성 요소의 마운트 해제나 음원 변경이 없으면 이 문제는 드러나지 않는다.

Reactv18 StrictMode 시 useEffect


다시 말하면 Reactv18부터 <StrictMode>에 둘러싸인 부품은 빌딩을 개발할 때만 설치할 때useffect라고 두 번 불린다.
알기 쉬운 글: https://www.pandanoir.info/entry/2021/07/11/143303

문제점


이 기능을 통해 상기 구성 요소는 설치할 때useEffect->청소->useEffect의 속도로 실행되며 다음과 같은 오류가 발생합니다.
  • 처음 로드 시작
  • 제1차 청결의 집행
  • 2차 로딩 시작
  • 첫 번째 로드가 완료된 후 중지되어 오류가 발생했습니다
  • 2차 로드 완료 후 사운드 재현
  • 4에서 발생한 것은 크롬의 다음 오류입니다.
    AbortError: The play() request was interrupted by a call to pause().
    
    play()의 준비가 완료되었을 때pause()까지 불렀을 경우 상기 오류가 발생하여 재생이 취소됩니다.
    이 경우 이 같은 오류를 무시하면 결과가 올바른 행동으로 바뀌지만 바람직한 실행방식은 아니다.

    인스턴스 개선


    길어지지만 아래와 같이 두 개의 로고를 사용하여 업로드 전에 정리 처리할 수 있습니다.
    Example3.tsx
    useEffect(() => {
      let isLoaded = false;
      let isCanceled = false;
      const audio = new Audio(soundSrc);
      audio
        .play()
        .then(() => {
          if (isCanceled) {
            // キャンセル済みの場合、ここで音停止
            audio.pause();
          } else {
            isLoaded = true;
          }
        })
        .catch((e) => alert(e));
    
      return () => {
        // ロード済みであれば音停止、そうでなければキャンセルフラグをセット
        if (isLoaded) {
          audio.pause();
        } else {
          isCanceled = true;
        }
      };
    }, [soundSrc]);
    
    isLoaded의 로고만 사용해 제어할 경우 소리가 중복되므로 주의가 필요합니다(CodeSandbox의Example2.

    useEffect는 동기화 함수만 설정할 수 있습니다


    참고로 전체를 async 함수로 설정하면 아래처럼 간단하게 쓸 수 있지만 useEffect는 동기화 함수만 설정할 수 있기 때문에 이걸 쓸 수 없습니다.
    // NG
    useEffect(async () => {
      const audio = new Audio(soundSrc);
      try {
        await audio.play();
      } catch (e) {
        return alert(e);
      }
    
      // audio.play()をawaitしてからクリーンアップ関数を返す
      return () => audio.pause();
    }, [soundSrc]);
    

    총결산


    useEffect에서 청소가 필요한 비동기 처리를 할 때 비동기 처리가 완료되기 전에 청소를 실행해야 하는 상황을 고려해야 하는 예를 보여 줍니다.
    이번에 StrictMode를 사용해서 문제점을 발견했습니다. 가능하면 StrictMode를 사용해서useEffect를 사용해야 하는지 확인하는 것이 좋습니다.

    좋은 웹페이지 즐겨찾기