react挂钩中的useEffect执行顺序及其内部清理逻辑是什么? [英] What's useEffect execution order and its internal clean-up logic in react hooks?

查看:228
本文介绍了react挂钩中的useEffect执行顺序及其内部清理逻辑是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据反应文档,useEffect将在重新运行useEffect部分之前触发清理逻辑.

According to react document, useEffect will trigger clean-up logic before it re-runs useEffect part.

如果您的效果返回了一个函数,React会在需要清理时运行它...

If your effect returns a function, React will run it when it is time to clean up...

没有特殊的代码来处理更新,因为默认情况下useEffect会处理它们.在应用下一个效果之前,它会清理之前的效果...

There is no special code for handling updates because useEffect handles them by default. It cleans up the previous effects before applying the next effects...

但是,当我在useEffect中使用requestAnimationFramecancelAnimationFrame时,我发现cancelAnimationFrame可能无法正常停止动画.有时,我发现旧动画仍然存在,而下一个效果带来了另一个动画,这导致了我的Web应用程序性能问题(尤其是当我需要渲染沉重的DOM元素时).

However, when I use requestAnimationFrame and cancelAnimationFrame inside useEffect, I found the cancelAnimationFrame may not stop the animation normally. Sometimes, I found the old animation still exists, while the next effect brings another animation, which causes my web app performance issues (especially when I need to render heavy DOM elements).

我不知道React钩子是否会在执行清理代码之前做一些额外的事情,这会使我的取消动画部分无法正常工作,useEffect钩子会做类似闭包的操作来锁定状态变量?

I don't know whether react hook will do some extra things before it executes the clean-up code, which make my cancel-animation part not work well, will useEffect hook do something like closure to lock the state variable?

useEffect的执行顺序及其内部清理逻辑是什么?我在下面编写的代码有什么问题,这使cancelAnimationFrame无法完美运行吗?

What's useEffect's execution order and its internal clean-up logic? Is there something wrong the code I write below, which makes cancelAnimationFrame can't work perfectly?

谢谢.

//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>

推荐答案

将这三行代码放入组件中,您将看到它们的优先级顺序.

Put these three lines of code in a component and you'll see their order of priority.

  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的清除功能之前请求另一个动画帧引起的.

The problem you're experiencing is caused by loopRaf requesting another animation frame before the cleanup function for useEffect is executed.

进一步的测试表明,useLayoutEffect总是在requestAnimationFrame之前被调用,并且其清理函数在下一次执行之前被调用以防止重叠.

Further testing has shown that useLayoutEffect is always called before requestAnimationFrame and that its cleanup function is called before the next execution preventing overlaps.

useEffect更改为useLayoutEffect,它应该可以解决您的问题 问题.

Change useEffect to useLayoutEffect and it should solve your problem.

useEffectuseLayoutEffect按照它们在代码中出现的顺序被调用,就像useState调用一样.

useEffect and useLayoutEffect are called in the order they appear in your code for like types just like useState calls.

您可以通过运行以下几行来查看:

You can see this by running the following lines:

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

这篇关于react挂钩中的useEffect执行顺序及其内部清理逻辑是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆