programing

useEffect를 두 번 실행하는 이유와 React에서 적절하게 처리하는 방법

cafebook 2023. 3. 9. 22:21
반응형

useEffect를 두 번 실행하는 이유와 React에서 적절하게 처리하는 방법

계산대와 계산대가 있습니다.console.log()에 있어서useEffect모든 변화를 기록할 수 있지만useEffect두 번 호출을 받습니다.리액트 18을 사용하고 있습니다.다음은 제 프로젝트의 Code Sandbox와 아래 코드입니다.

import  { useState, useEffect } from "react";

const Counter = () => {
  const [count, setCount] = useState(5);

  useEffect(() => {
    console.log("rendered", count);
  }, [count]);

  return (
    <div>
      <h1> Counter </h1>
      <div> {count} </div>
      <button onClick={() => setCount(count + 1)}> click to increase </button>
    </div>
  );
};

export default Counter;

useEffect마운트 시 React 18 이후 두 번 호출되는 것은 정상입니다.development와 함께StrictMode다음은 매뉴얼에 기재되어 있는 내용의 개요입니다.

향후 React가 상태를 유지하면서 UI 섹션을 추가 및 제거할 수 있는 기능을 추가하고 싶습니다.예를 들어 사용자가 화면에서 멀리 떨어진 후 뒤로 탭하면 React는 이전 화면을 즉시 표시할 수 있어야 합니다.이를 위해 React는 마운트 해제 전에 사용한 것과 동일한 컴포넌트 상태를 사용하여 트리 재마운트를 지원합니다.

이 기능을 통해 React는 즉시 더 나은 성능을 제공하지만 여러 번 마운트되고 파괴되는 효과에 대한 내장해성이 필요한 구성 요소가 필요합니다.대부분의 효과는 변경 없이 작동하지만 일부 효과는 파기 콜백에서 서브스크립션이 제대로 정리되지 않거나 암묵적으로 마운트 또는 파기된 것으로 간주됩니다.

이러한 문제를 해결하기 위해 React 18은 엄격한 모드에 대한 새로운 개발 전용 체크를 도입했습니다.이 새 검사는 구성 요소가 처음 마운트될 때마다 모든 구성 요소를 자동으로 마운트 해제하고 다시 마운트하여 두 번째 마운트의 이전 상태를 복원합니다.

이것은 에만 해당됩니다.development모드,production동작은 변경되지 않습니다.

이상하다고 생각됩니다만, 최종적으로, HTTP 요구를 캐싱 해, 2개의 콜이 문제가 될 때마다 정리 기능을 사용하고, 버그가 없고, 현재의 가이드 라인에 맞추어 향후의 버전과 호환성이 있는, 보다 좋은 리액트 코드를 작성합니다.다음은 예를 제시하겠습니다.

/* Having a setInterval inside an useEffect: */

import { useEffect, useState } from "react";

const Counter = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const id = setInterval(() => setCount((count) => count + 1), 1000);

    /* 
       Make sure I clear the interval when the component is unmounted,
       otherwise, I get weird behavior with StrictMode, 
       helps prevent memory leak issues.
    */
    return () => clearInterval(id);
  }, []);

  return <div>{count}</div>;
};

export default Counter;

Resact 팀은 "효과와의 동기화"라는 매우 상세한 기사에서 다음과 같이 설명합니다.useEffect예를 들어 다음과 같이 기술합니다.

이는 재마운트로 인해 응용 프로그램의 논리가 깨지면 일반적으로 기존 버그가 발견된다는 것을 나타냅니다.사용자의 관점에서 페이지를 방문하는 것은 페이지를 방문하여 링크를 클릭한 다음 뒤로 누르는 것과 다를 수 없습니다.React는 컴포넌트를 개발 중에 재마운트함으로써 이 원칙을 위반하지 않는지 확인합니다.

특정 사용 사례에 대해서는 아무 걱정 없이 그대로 두셔도 됩니다.그리고 이러한 테크놀로지는 마십시오.useRef ★★★★★★★★★★★★★★★★★」ifuseEffect 번 을 지르다, StrictMode매뉴얼을 참조해 주십시오.

개발 중에 컴포넌트를 의도적으로 재마운트하여 버그를 검출할 수 있도록 합니다.올바른 질문은 "효과 한 번 실행하는 방법"이 아니라 "재마운트 후 효과가 작동하도록 수정하는 방법"입니다.

일반적으로 정리 기능을 구현하는 것이 답입니다.이펙트가 하고 있던 모든 작업을 정리 기능이 중지 또는 취소해야 합니다.경험의 법칙은 사용자가 한 번 실행한 효과(생산 시처럼)와 설정 → 정리 → 설정 시퀀스(개발 시처럼)를 구분할 수 없다는 것입니다.

/* As a second example, an API call inside an useEffect with fetch: */

useEffect(() => {
  const abortController = new AbortController();

  const fetchUser = async () => {
    try {
      const res = await fetch("/api/user/", {
        signal: abortController.signal,
      });
      const data = await res.json();
    } catch (error) {
      if (error.name !== "AbortError") {
        /* Logic for non-aborted error handling goes here. */
      }
    }
  };

  fetchUser();

  /* 
    Abort the request as it isn't needed anymore, the component being 
    unmounted. It helps avoid, among other things, the well-known "can't
    perform a React state update on an unmounted component" warning.
  */
  return () => abortController.abort();
}, []);

이미 발생한 네트워크 요청을 "실행 취소"할 수는 없지만, 더 이상 관련이 없는 가져오기 기능이 애플리케이션에 계속 영향을 미치지 않도록 해야 합니다.

개발 시 [Network]탭에 2개의 페치가 표시됩니다.그건 문제될 게 없어요.위의 접근방식에서는 첫 번째 효과가 즉시 청소됩니다.따라서 추가 요청이 있더라도 중단으로 인한 상태에는 영향을 미치지 않습니다.

프로덕션에서는 요청은 1개뿐입니다.개발 중인 두 번째 요청이 번거로운 경우 요청을 중복 제거하여 컴포넌트 간에 응답을 캐시하는 솔루션을 사용하는 것이 가장 좋습니다.

function TodoList() {
  const todos = useSomeDataFetchingLibraryWithCache(`/api/user/${userId}/todos`);
  // ...

업데이트: 이 게시물을 돌아보면, 좀 더 현명하게, 제발 이것을 하지 말아주세요.

사용하다ref 커스텀을 수도 있습니다.hook아, 아, 아, 아, 아, 아, 아, 네.

import type { DependencyList, EffectCallback } from 'react';
import { useEffect } from 'react';

const useClassicEffect = import.meta.env.PROD
  ? useEffect
  : (effect: EffectCallback, deps?: DependencyList) => {
      useEffect(() => {
        let subscribed = true;
        let unsub: void | (() => void);

        queueMicrotask(() => {
          if (subscribed) {
            unsub = effect();
          }
        });

        return () => {
          subscribed = false;
          unsub?.();
        };
      }, deps);
    };

export default useClassicEffect;

언급URL : https://stackoverflow.com/questions/72238175/why-useeffect-running-twice-and-how-to-handle-it-well-in-react

반응형