programing

왜 모든 렌더에서 useEffect의 정리 기능이 호출됩니까?

showcode 2023. 4. 5. 22:31
반응형

왜 모든 렌더에서 useEffect의 정리 기능이 호출됩니까?

리액트라는 가 리액트라는 .useEffect는 정리를 수행하도록 되어 있으며 구성 요소가 마운트 해제되면 React가 정리를 수행합니다.

을 해봤는데 이 함수는 DOM에서 분리되었을 때에만 호출됩니다.console.log("unmount");이치노

왜 그런 것일까요?

function Something({ setShow }) {
  const [array, setArray] = useState([]);
  const myRef = useRef(null);

  useEffect(() => {
    const id = setInterval(() => {
      setArray(array.concat("hello"));
    }, 3000);
    myRef.current = id;
    return () => {
      console.log("unmount");
      clearInterval(myRef.current);
    };
  }, [array]);

  const unmount = () => {
    setShow(false);
  };

  return (
    <div>
      {array.map((item, index) => {
        return (
          <p key={index}>
            {Array(index + 1)
              .fill(item)
              .join("")}
          </p>
        );
      })}
      <button onClick={() => unmount()}>close</button>
    </div>
  );
}

function App() {
  const [show, setShow] = useState(true);

  return show ? <Something setShow={setShow} /> : null;
}

라이브 예:https://codesandbox.io/s/vigilant-leavitt-z1jd2

구성 요소가 마운트 해제되면 React가 정리를 수행합니다.

어디서 읽었는지 모르겠지만 이 문장은 틀렸어요.React는 해당 후크에 대한 의존관계가 변경되어 효과 후크를 새 값으로 다시 실행해야 할 때 청소를 수행합니다.이 동작은 데이터 변경에 대한 뷰의 반응성을 유지하기 위한 의도입니다.공식적인 예에서 벗어나, 앱이 친구의 프로필에서 상태 업데이트를 구독한다고 가정해 봅시다.당신이 훌륭한 친구이기 때문에, 당신은 그들에게 불친절하게 대하고 다른 누군가와 친구가 되기로 결심한다.이제 앱은 이전 친구의 상태 업데이트를 구독하지 않고 새 친구의 업데이트를 들어야 합니다.이것은 자연스럽고 쉬운 방법으로 달성할 수 있습니다.useEffect

 useEffect(() => { 
    chatAPI.subscribe(props.friend.id);

    return () => chatAPI.unsubscribe(props.friend.id);
  }, [ props.friend.id ])

의존관계 목록에 친구 ID를 포함하면 친구 ID가 변경되었을 때만 후크를 실행할 필요가 있음을 나타낼 수 있습니다.

에서는 '어울리다', '어울리다', '어울리다'를 했습니다.array재실행됩니다.어레이를 변경할 때마다 후크가 다시 실행됩니다.

하고 의 할 수 .setState은 항상 변경될 . 콜백버전은 항상 이전 버전의 스테이트에서 동작하기 때문에 어레이가 변경될 때마다 훅을 새로 고칠 필요가 없습니다.

  useEffect(() => {
    const id = setInterval(() => setArray(array => [ ...array, "hello" ]), 3000);

    return () => {
      console.log("unmount");
      clearInterval(id);
    };
  }, []);

은 ID에 있는 입니다.clearInterval이 값은 정리 기능을 만들 때 닫힙니다(삭제).참조인에게 저장할 필요가 없습니다.

React 문서에는 이에 대한 설명 섹션이 있습니다.

즉, 이러한 설계는 오래된 데이터와 업데이트 버그로부터 보호하기 때문입니다.

useEffect훅 in React는 초기 렌더링과 후속 렌더를 모두 처리하도록 설계되었습니다(자세한 내용은 여기를 참조하십시오).


효과는 그것을 사용하는 컴포넌트의 라이프 사이클이 아니라 의존성을 통해 제어됩니다.

이 바뀔 때마다, anytime과의 anytime anytime anytime anytime anytime anytime anytime anytime anytime anytime anytime anytime anytime anytime anytime anytime anytime anytime anytime.useEffect이전 효과를 정리하고 새로운 효과를 실행합니다.

이러한 설계는 보다 예측하기 쉬우며, 각 렌더에는 독자적인 (순수한) 동작 효과가 있습니다.이렇게 하면 UI가 항상 올바른 데이터를 표시합니다(React의 mental 모델의 UI는 특정 렌더에 대한 상태를 스크린샷으로 표시).

우리가 효과를 조절하는 방법은 그들의 의존성을 통해서다.

모든 렌더에서 정리가 실행되지 않도록 하려면 효과의 종속성을 변경하지 않으면 됩니다.

청소가 이루어지기 때문입니다.array하고 있다,즉 변화하고 있다, 변화하고 있다, 변화하고 있다, 변화하고 있다,Object.is(oldArray, newArray) === false

useEffect(() => {
  // ...
}, [array]);
//  ^^^^^ you're changing the dependency of the effect

이 변경은 다음 행에서 발생합니다.

useEffect(() => {
  const id = setInterval(() => {
    setArray(array.concat("hello")); // <-- changing the array changes the effect dep
  }, 3000);
  myRef.current = id;

  return () => {
    clearInterval(myRef.current);
  };
}, [array]); // <-- the array is the effect dep

다른 사람들이 말했듯이 useEffect는 useEffect의 두 번째 파라미터로 지정된 "배열"의 변경에 의존합니다.따라서 빈 배열로 설정하면 컴포넌트가 마운트되었을 때 useEffect를 한 번 트리거할 수 있습니다.

여기서 중요한 것은 어레이의 이전 상태를 변경하는 것입니다.

setArray((arr) => arr.concat("hello"));

아래를 참조해 주세요.

  useEffect(() => {
     const id = setInterval(() => {
         setArray((arr) => arr.concat("hello"));
     }, 3000);
     myRef.current = id;
     return () => {
        console.log("unmount");
        clearInterval(myRef.current);
     };
  }, []);

데모용으로 CodeSandbox https://codesandbox.io/s/heuristic-maxwell-gcuf7?file=/src/index.fork를 사용하였습니다.

두 매개 변수 할 수 .[array]업데이트 중이므로 재렌더를 호출합니다.빈 어레이를 설정해 보십시오.

모든 상태 업데이트는 재렌더와 마운트를 호출하고 어레이가 변경됩니다.

예상했던 것 같아요.에 기재되어 있는 , 「 」useEffect는 첫 번째 렌더링 후 업데이트 및 마운트 해제될 때마다 호출됩니다.

https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

팁.

React 클래스의 라이프 사이클 메서드에 익숙한 경우 useEffect Hook을 componentDidMount, componentDidUpdate 및 componentWillUnmount 결합 전으로 생각할 수 있습니다.

렌더링 순서와 효과 순서를 나타내는 Jest 테스트입니다.

가 되면, 의존 관계가 됩니다.foo상태 업데이트로 인해 변경되면 새 렌더가 트리거되고 첫 번째 렌더의 정리 기능이 트리거됩니다.

  it("with useEffect async set state and timeout and cleanup", async () => {
    jest.useFakeTimers();
    let theRenderCount = 0;
    const trackFn = jest.fn((label: string) => { });
    function MyComponent() {
      const renderCount = theRenderCount;
      const [foo, setFoo] = useState("foo");
      useEffect(() => {
        trackFn(`useEffect ${renderCount}`);
        (async () => {
          await new Promise<string>((resolve) =>
            setTimeout(() => resolve("bar"), 5000)
          );
          setFoo("bar");
        })();
        return () => trackFn(`useEffect cleanup ${renderCount}`);
      }, [foo]);
      ++theRenderCount;
      trackFn(`render ${renderCount}`);
      return <span data-testid="asdf">{foo}</span>;
    }
    const { unmount } = render(<MyComponent></MyComponent>);
    expect(screen.getByTestId("asdf").textContent).toBe("foo");
    jest.advanceTimersByTime(4999);
    expect(screen.getByTestId("asdf").textContent).toBe("foo");
    jest.advanceTimersByTime(1);
    await waitFor(() =>
      expect(screen.getByTestId("asdf").textContent).toBe("bar")
    );

    trackFn("before unmount");
    unmount();
    expect(trackFn.mock.calls).toEqual([
      ['render 0'],
      ['useEffect 0'],
      ['render 1'],
      ['useEffect cleanup 0'],
      ['useEffect 1'],
      ['before unmount'],
      ['useEffect cleanup 1']
    ])
  });

언급URL : https://stackoverflow.com/questions/57023074/why-is-the-cleanup-function-from-useeffect-called-on-every-render

반응형