【问题标题】:Behaviour of a function in useEffect when different browser tab is active当不同的浏览器选项卡处于活动状态时,useEffect 中函数的行为
【发布时间】:2020-08-13 16:07:44
【问题描述】:

我有一个奇怪的问题。 我创建了一个函数,旨在在 60 秒后重置 Linearprogress 元素。


useEffect(() => {

    const interval2 = setInterval(() => {

        var internal = timer

        if( internal < 100 ) {internal = (internal - (1.695 * -1 )) } else {internal = internal - 100}

       setTimer(internal)
       
     
    

    }, 1000)
    return () => clearInterval(interval2)
  }, [timer])

然后,我有一个像这样的线性进度元素的渲染:


return (
    <div>
  
        <LinearProgress
         color ="secondary"
          value={timer}
          variant="determinate"
        />
      
    </div>
  );

现在奇怪的部分:当我查看我的应用程序时看起来一切正常,60 秒后栏重置开始并重复。但是,当我在重置后更改浏览器中的活动选项卡并在 55 秒后返回时(该栏应接近末尾) - 该栏位于中间。

当带有应用程序的选项卡未处于活动状态时,useeffect 似乎没有像应有的那样频繁地重新执行该功能。

我在这里错过了什么。

CODE SANDBOX(问题在那里复制):https://codesandbox.io/s/young-brook-mttpz?file=/src/App.js:205-206

谢谢

【问题讨论】:

    标签: javascript reactjs material-ui use-effect


    【解决方案1】:

    由于您的setInterval,您有内存泄漏。 每 1000 毫秒,它会重新运行,但同时,您的 useEffect 也被 setTimer(internal); 触发。所以你有越来越多的 setInterval 运行。

    一种解决方案是在更新Timer 之前添加clearInterval(interval2);

    但从概念上讲它并不完美,因为我们使用间隔作为超时,因此您只需将 setInterval 替换为 setTimeout 并在返回 clearInterval 中替换为 clearTimeout 即可,无需修改任何其他内容。

    这是您的代码经过修改的工作版本和sandbox

    import React from "react";
    import PropTypes from "prop-types";
    import { makeStyles } from "@material-ui/styles";
    import { LinearProgress } from "@material-ui/core";
    import { useEffect } from "react";
    
    const TotalProfit = (props) => {
      const [timer, setTimer] = React.useState(0);
    
      useEffect(() => {
        const interval2 = setTimeout(() => {
          var internal = timer;
    
          if (internal < 100) {
            internal = internal - 1.695 * -1;
          } else {
            internal = internal - 100;
          }
    
          setTimer(internal);
        }, 1000);
        return () => clearTimeout(interval2);
      }, [timer]);
    
      return (
        <div>
          <div>{timer}</div>
          <LinearProgress color="secondary" value={timer} variant="determinate" />
        </div>
      );
    };
    
    TotalProfit.propTypes = {
      className: PropTypes.string
    };
    
    export default TotalProfit;
    

    正如here 所解释的那样,浏览器分配给非焦点标签的资源较少,因此计时器可能是错误的。因此,给出的解决方案之一是使用在首次渲染组件时初始化的计时器。然后你使用 Date.now() 和你的第一次渲染时间之间的差异来获得你的间隔(模 100)(sandbox)。

    import React, { useEffect, useState } from "react";
    import PropTypes from "prop-types";
    import { makeStyles } from "@material-ui/styles";
    import { LinearProgress } from "@material-ui/core";
    
    const TotalProfit = (props) => {
      const [timer] = useState(Date.now()/1000);
      const [delta, setDelta] = useState(0);
    
      useEffect(() => {
        const interval2 = setInterval(() => {
          setDelta((Date.now()/1000 - timer) % 100);
        }, 1000);
        return () => clearInterval(interval2);
      }, [timer]);
    
      return (
        <div>
          <div>{delta}</div>
          <LinearProgress color="secondary" value={delta} variant="determinate" />
        </div>
      );
    };
    
    TotalProfit.propTypes = {
      className: PropTypes.string
    };
    
    export default TotalProfit;
    

    否则,如果您的目标只是拥有一个加载器,您可以使用如图所示的 css 动画(稍作修改以获得与 js 相同的渲染)here

    body {margin: 0; padding: 0;}
    @keyframes loader-animation {
      0% {
        width: 0%;
      }
      100% {
        width: 100%;
        left: 0%
      }
    }
    .loader {
      height: 5px;
      width: 100%;
    }
    .loader .bar {
      position: absolute;
      height: 5px;
      background-color: dodgerblue;
      animation-name: loader-animation;
      animation-duration: 3s;
      animation-iteration-count: infinite;
      animation-timing-function: ease-in-out;
    }
    <div class="loader">
      <div class="bar"></div>
    </div>

    【讨论】:

    • 嘿,我了解这个问题,但是无法按照您的建议通过更改代码来解决它。为什么只有在标签没有聚焦时才会显示泄漏?
    • 您能否告诉我是否可以在我添加到答案的沙箱中重现该问题?我试过但无法重现它。我猜测选项卡重新获得焦点时发生的问题是由于浏览器的一些内存管理(在用户不观看选项卡时限制了一些渲染)。
    • 是的,我的问题仍然存在。在 17:54:00 打开应用程序,将标签更改为我的 facebook,在 17:54:55 返回 - 并且栏显示计时器 = 50(中间的进度条) - 正如我所说的那样,当我的活动选项卡是沙箱。你能重现它吗?
    • 刚刚看到你说你不能复制它......谢谢!
    • 我尝试了一个稍微不同的版本,发现我也有这个错误(但认为这不是因为internal = internal - 1.695 * -1;。我在stackoverflow上找到了another question关于同样的问题。解决方案将是使用requestAnimationFrame。我不知道你的代码的上下文,但如果你只想填充一行并重置一次,你只能用css来做到这一点。我用这个更新了我的答案信息。
    猜你喜欢
    • 2011-10-22
    • 1970-01-01
    • 1970-01-01
    • 2017-11-19
    • 1970-01-01
    • 2021-05-25
    • 2010-12-18
    • 2020-11-23
    相关资源
    最近更新 更多