在我的 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 代码。但它并没有解决问题。计时器仍然冻结。我想不出另一种方法。有什么想法如何解决这个问题吗?
找出问题所在。在我的自定义挂钩中,我添加了功能依赖项:Set_value 和 Expired_callback。 Expired_callback 导致计时器冻结,因为它可能是在重新渲染期间重新创建的,并且它强制钩子内的 useEffect 首先调用其返回回调(清除间隔)并使用新参数执行其主体。频繁的重新渲染让人震惊。从 hooks 依赖项数组中删除功能依赖项 Expired_callback 解决了这个问题。 更新:将在自定义挂钩中传递的实际函数包装为 useCallback() 反应挂钩中应用程序组件中的“Expired_callback”也可以解决问题。