如何修复 useEffect 中的计时器在频繁重新渲染时冻结的问题?

问题描述 投票:0回答:1

在我的 React 应用程序中,我有高度交互的组件,其中用户键盘输入上发生了大量频繁的重新渲染。我还想在这个组件中表示计时器。我有一个自定义钩子,它包装

useEffect
以提供间隔设置功能来创建计时器。 hook的代码如下:

import { useEffect } from "react";

export function Use_intervaled_set_action<T>(
  value: T,
  Set_value: React.Dispatch<React.SetStateAction<T>>,
  Set_action: (prev: T) => T,
  Continue_condition: (arg: T) => boolean,
  Expired_callback: () => void,
  running: boolean
) : void {
  useEffect(() => {
    const interval = setInterval(() => {
      if (running && Continue_condition(value)) { Set_value(Set_action(value)); }
    }, 1000);

    if (!Continue_condition(value)) Expired_callback();

    return () => clearInterval(interval);
  }, [value, Set_value, Expired_callback, running]);
}

当没有输入时,时间运行得很好。但是,一旦用户开始频繁地按下键盘按键,就会开始发生大量重新渲染并冻结计时器。 为了代表这个问题,我构建了一个使用上述钩子的示例作为一个简单的独立“应用程序”组件。

代码如下:

import { useState, useRef, useEffect } from 'react'
import { Use_intervaled_set_action } from './Use_intervaled_set_action';

function App() {
  const [timer, Set_timer] = useState(() => 120);
  const [running, Set_running] = useState(() => false);
  const [rerenders, Set_rerenders] = useState(() => 0);
  const timer_configuration = useRef({
    timer_action: (prev: number) => prev - 1,
    timer_continue_condition: (arg: number) => arg > 0
  });

  Use_intervaled_set_action(
    timer,
    Set_timer,
    timer_configuration.current.timer_action,
    timer_configuration.current.timer_continue_condition,
    () => alert('Expired'),
    running
  );

  const Handle_space = () => {
    Set_rerenders((prev) => prev + 1);
  };

  useEffect(() => {
    document.addEventListener('keydown', Handle_space)
    return () => {document.removeEventListener('keydown', Handle_space)};
  });

  return (
    <>
    <span>{timer}</span>
    <br/>
    <button onClick={() => Set_running((r) => !r)}>Click me to toogle timer. Now {running ? 'running' : 'paused'}.</button>
    <br/>
    <button onClick={() => Set_timer(() => 10)}>Click me to set timer to 10.</button>
    <br/>
    <button onClick={() => Set_rerenders((prev) => prev + 1)}>Click me or press 'space' to rerender. Rerendered: {rerenders} times.</button>
    </>
  );
}

export default App;

当我按住空格或频繁按下“单击我重新渲染”按钮时,计时器停止运行。有什么办法可以防止频繁重新渲染而停止计时器运行吗?

我唯一尝试的是将 hook 更改为

async
,因为我认为缺少
async
会使其在重新渲染之间运行,而实际上有时间运行我的 hooks 代码。但它并没有解决问题。计时器仍然冻结。我想不出另一种方法。有什么想法如何解决这个问题吗?

reactjs typescript setinterval
1个回答
0
投票

找出问题所在。在我的自定义挂钩中,我添加了功能依赖项:Set_value 和 Expired_callback。 Expired_callback 导致计时器冻结,因为它可能是在重新渲染期间重新创建的,并且它强制钩子内的 useEffect 首先调用其返回回调(清除间隔)并使用新参数执行其主体。频繁的重新渲染让人震惊。从 hooks 依赖项数组中删除功能依赖项 Expired_callback 解决了这个问题。 更新:将在自定义挂钩中传递的实际函数包装为 useCallback() 反应挂钩中应用程序组件中的“Expired_callback”也可以解决问题。

© www.soinside.com 2019 - 2024. All rights reserved.