programing

request Animation Frame과 cancel Animation Frame을 사용할 때 use Effect 실행 순서와 내부 정리 로직은 무엇입니까?

testmans 2023. 3. 1. 09:42
반응형

request Animation Frame과 cancel Animation Frame을 사용할 때 use Effect 실행 순서와 내부 정리 로직은 무엇입니까?

문서에 , 액액 according according according according 。useEffect는, 「 로직」을 하고 , 「Clean-up 」을 재실행합니다.useEffectsyslog.syslog.

이펙트가 함수를 반환할 경우 React는 정리할 시간이 되면 해당 기능을 실행합니다.

.왜냐하면 업데이트 처리에는 특별한 코드가 없습니다.useEffect는 디폴트로 그것들을 처리합니다.다음 효과를 적용하기 전에 이전 효과를 청소합니다.

, ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★」requestAnimationFrame ★★★★★★★★★★★★★★★★★」cancelAnimationFrame에 inside inside inside useEffect Animation 하지 않을 수 cancel Animation Frame은 애니메이션 프레임을 취소합니다.때로는 오래된 애니메이션이 아직 존재하지만 다음 효과로 인해 다른 애니메이션이 생겨 웹 앱 성능 문제가 발생할 수 있습니다(특히 무거운 DOM 요소를 렌더링해야 할 경우).

하기 전에, 리액트 훅이 몇 의 취소 잘 하지 않게 .이것에 의해, 제 취소 애니메이션 부분이 제대로 동작하지 않게 됩니다.useEffect상태 변수를 잠그기 위해 폐쇄와 같은 것을 할 수 있습니까?

useEffect의 실행 순서와 내부 정리 로직은 무엇입니까?cancel Animation Frame이 완벽하게 동작하지 않는 아래에 기재되어 있는 코드에 문제가 있습니까?

감사해요.

//import React, { useState, useEffect } from "react";

const {useState, useEffect} = React;

//import ReactDOM from "react-dom";

function App() {
  const [startSeconds, setStartSeconds] = useState(Math.random());
  const [progress, setProgress] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setStartSeconds(Math.random());
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  useEffect(
    () => {
      let raf = null;

      const onFrame = () => {
        const currentProgress = startSeconds / 120.0;
        setProgress(Math.random());
        // console.log(currentProgress);
        loopRaf();
        if (currentProgress > 100) {
          stopRaf();
        }
      };

      const loopRaf = () => {
        raf = window.requestAnimationFrame(onFrame);
        // console.log('Assigned Raf ID: ', raf);
      };

      const stopRaf = () => {
        console.log("stopped", raf);
        window.cancelAnimationFrame(raf);
      };

      loopRaf();

      return () => {
        console.log("Cleaned Raf ID: ", raf);
        // console.log('init', raf);
        // setTimeout(() => console.log("500ms later", raf), 500);
        // setTimeout(()=> console.log('5s later', raf), 5000);
        stopRaf();
      };
    },
    [startSeconds]
  );

  let t = [];
  for (let i = 0; i < 1000; i++) {
    t.push(i);
  }

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <text>{progress}</text>
      {t.map(e => (
        <span>{progress}</span>
      ))}
    </div>
  );
}

ReactDOM.render(<App />,
document.querySelector("#root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.7.0-alpha.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>

위의 답변에서 명확하지 않은 것은 여러 컴포넌트가 혼합되어 있을 때 효과가 실행되는 순서입니다.우리는 useContext를 통해 부모와 자녀의 코디네이션이 필요한 작업을 해왔기 때문에 순서가 더 중요합니다. useLayoutEffect ★★★★★★★★★★★★★★★★★」useEffect이 점에 있어서, 다른 방법으로 작업합니다.

useEffect는 다음 컴포넌트(깊이 먼저)로 이동하여 동일한 작업을 수행하기 전에 정리 및 새로운 효과를 수행합니다.

useLayoutEffect는 각 컴포넌트의 청소(심도 우선)를 실행한 후 모든 컴포넌트의 새로운 효과(심도 우선)를 실행합니다.

render parent
render a
render b
layout cleanup a
layout cleanup b
layout cleanup parent
layout effect a
layout effect b
layout effect parent
effect cleanup a
effect a
effect cleanup b
effect b
effect cleanup parent
effect parent
const Test = (props) => {
  const [s, setS] = useState(1)

  console.log(`render ${props.name}`)

  useEffect(() => {
    const name = props.name
    console.log(`effect ${props.name}`)
    return () => console.log(`effect cleanup ${name}`)
  })

  useLayoutEffect(() => {
    const name = props.name
    console.log(`layout effect ${props.name}`)
    return () => console.log(`layout cleanup ${name}`)
  })

  return (
    <>
      <button onClick={() => setS(s+1)}>update {s}</button>
      <Child name="a" />
      <Child name="b" />
    </>
  )
}

const Child = (props) => {
  console.log(`render ${props.name}`)

  useEffect(() => {
    const name = props.name
    console.log(`effect ${props.name}`)
    return () => console.log(`effect cleanup ${name}`)
  })

  useLayoutEffect(() => {
    const name = props.name
    console.log(`layout effect ${props.name}`)
    return () => console.log(`layout cleanup ${name}`)
  })

  return <></>
}

이 세 줄의 코드를 컴포넌트에 넣으면 우선순위가 표시됩니다.

  useEffect(() => {
    console.log('useEffect')
    return () => {
      console.log('useEffect cleanup')
    }
  })

  window.requestAnimationFrame(() => console.log('requestAnimationFrame'))

  useLayoutEffect(() => {
    console.log('useLayoutEffect')
    return () => {
      console.log('useLayoutEffect cleanup')
    }
  })

useLayoutEffect > requestAnimationFrame > useEffect

있는 는 바로 이 문제로 인한 입니다.loopRaf 에 다른 useEffect행됩니니다다

, ★★★★★★★★★★★★★★★★★★★★★★★★★★★★,useLayoutEffect 앞에는 요.requestAnimationFrame그리고 그 정리기능이 다음 실행 전에 호출되어 중복이 방지된다.

바꾸다useEffect로.useLayoutEffect그러면 문제가 해결될 거야

useEffect그리고.useLayoutEffect코드에 표시되는 순서대로 호출됩니다.useState콜을 클릭합니다.

다음 행을 실행하면 이를 확인할 수 있습니다.

  useEffect(() => {
    console.log('useEffect-1')
  })
  useEffect(() => {
    console.log('useEffect-2')
  })
  useLayoutEffect(() => {
    console.log('useLayoutEffect-1')
  })
  useLayoutEffect(() => {
    console.log('useLayoutEffect-2')
  })

후크를 사용할 때와 라이프 사이클 기능을 실장할 때 주의해야 할 후크는 2종류가 있습니다.

문서에 따르면:

useEffectreact 후에 실행되며 컴포넌트를 렌더링하여 효과 콜백이 브라우저 페인팅을 차단하지 않도록 합니다.이것은 클래스 컴포넌트의 동작과 다릅니다.componentDidMount그리고.componentDidUpdate렌더링 후 동기적으로 실행됩니다.

따라서 를 사용하여requestAnimationFrame이러한 라이프 사이클에서는, 겉보기에는 별로 효과가 없지만, 약간 문제가 있다.useEffect따라서 useEffect는 사용자가 변경해야 하는 변경이 응답 수신 후 DOM의 변경을 초래하는 API 호출과 같은 시각적 업데이트를 차단하지 않는 경우에 사용해야 합니다.

가 없지만 할 때 한 또 는 DOM입니다.useLayoutEffect문서와 같다.

시그니처는 useEffect와 동일하지만 모든 DOM 변환 후 동기적으로 실행됩니다.이를 통해 DOM에서 레이아웃을 읽고 동기적으로 재렌더합니다.「 」내의 되고 .useLayoutEffect브라우저가 페인트를 칠하기 전에 동기식으로 플러시됩니다.

따라서 효과가 (DOM 노드 참조를 통해) DOM을 변환하고 DOM 변환에 의해 렌더링된 시점부터 효과가 변환된 시점까지 DOM 노드의 외관이 변경되는 경우 를 사용하지 않습니다.사용하고 싶을 것입니다.useLayoutEffect 않으면 때 깜박임이 수 돌연변이는 DOM의 .것은,, 의의같같다다 다다다다다requestAnimationFrame

//import React, { useState, useEffect } from "react";

const {useState, useLayoutEffect} = React;

//import ReactDOM from "react-dom";

function App() {
  const [startSeconds, setStartSeconds] = useState("");
  const [progress, setProgress] = useState(0);

  useLayoutEffect(() => {
    setStartSeconds(Math.random());

    const interval = setInterval(() => {
      setStartSeconds(Math.random());
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  useLayoutEffect(
    () => {
      let raf = null;

      const onFrame = () => {
        const currentProgress = startSeconds / 120.0;
        setProgress(Math.random());
        // console.log(currentProgress);
        loopRaf();
        if (currentProgress > 100) {
          stopRaf();
        }
      };

      const loopRaf = () => {
        raf = window.requestAnimationFrame(onFrame);
        // console.log('Assigned Raf ID: ', raf);
      };

      const stopRaf = () => {
        console.log("stopped", raf);
        window.cancelAnimationFrame(raf);
      };

      loopRaf();

      return () => {
        console.log("Cleaned Raf ID: ", raf);
        // console.log('init', raf);
        // setTimeout(() => console.log("500ms later", raf), 500);
        // setTimeout(()=> console.log('5s later', raf), 5000);
        stopRaf();
      };
    },
    [startSeconds]
  );

  let t = [];
  for (let i = 0; i < 1000; i++) {
    t.push(i);
  }

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <text>{progress}</text>
      {t.map(e => (
        <span>{progress}</span>
      ))}
    </div>
  );
}

ReactDOM.render(<App />,
document.querySelector("#root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.7.0-alpha.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>

언급URL : https://stackoverflow.com/questions/53781632/whats-useeffect-execution-order-and-its-internal-clean-up-logic-when-requestani

반응형