Keydown 事件侦听器导致 ReactJS 中的滞后、挂起、DOM 颠簸
Keydown event listener causes lagging, hanging, DOM thrashing in ReactJS
我正在 运行 在本地创建一个站点,并在 React 框架中部署了一个 80 页的模态。我使用状态通过 [currentStep, setCurrentStep]
挂钩在模态页面中移动。 setCurrentStep 可以通过三种方式调用——通过后退和下一步单击按钮,通过输入页码然后回车的输入框,以及通过左右键盘键。只有左右键给我一个问题。多次单击它们(中间有停顿)后,页面变得无响应。
我想这可能与监听器、阻塞或太多未完成的调用有关,我想我也许可以通过内存和事件监听器等开发工具的功能找到答案,但这些对我没有帮助。我注意到输入框选项也使用了类似的事件侦听器,所以我认为可能会有某种交互,所以我将其更改为 keyup 侦听器并且仍然具有相同的效果。
然后我注意到,当我单击一个箭头时,DOM 开始抖动:在两三页之间来回安装和卸载一些同级元素,尽管速度很快,但我还是可以挑出来。从一开始就这样做,我第一次注意到,这是正常的。第二次它只打了几下,然后就越来越高,几乎就像是一个 n!功能。
有趣的是,其他两个方法(按钮和输入)调用完全相同的 setCurrentStep,仅此而已。一定有一些副作用发生或某处有一些差异,这让人认为需要安装和卸载,尽管它们经过相同的狭窄路径。我肯定错过了什么。我很确定这不是数据类型问题。 next 和 back 函数处理递增和递减。
import React, { ReactNode, useState } from "react";
import { QuestionsObject } from './QuestionData/raw/QuestionCompiler'
import { ViewContainer } from './Views/ViewContainer'
const MultiStepAssessmentForm = (props) =>
{
const [currentStep, setCurrentStep] = useState(0);
let questionsArray = Object.keys(QuestionsObject)
let qIndex = questionsArray[currentStep]
const handleKeyDown = function (e)
{
if (e.code == 'NumpadEnter'){
setCurrentStep(parseInt(e.target.value))
e.target.value = ''
}
}
const next = () => { setCurrentStep(currentStep + 1) };
const back = () => { setCurrentStep(currentStep - 1); };
document.body.addEventListener('keydown', (e) =>
{
if (e.code == 'ArrowRight') next()
if (e.code == 'ArrowLeft' && currentStep > 0) back()
})
const formProps = {
data: formData,
handleInputBoxChange: handleInputBoxChange,
handleKeyValueCHange: handleKeyValueChange,
currentStep: currentStep,
next: next,
back: back,
}
function renderQuestions()
{
return (
<div className="temp Dev Div">
<input type="text" placeholder="Go to page X" onKeyDown={(e) => { handleKeyDown(e) }} />
<ViewContainer formProps={formProps} question={QuestionsObject[qIndex]} />
</div>
)
}
return (
<> {renderQuestions()}</>
)
};
export default MultiStepAssessmentForm;
我最好的猜测是,这是因为我将侦听器附加到主体上,这与 React 混淆了。这是我看到的两个差异之一,也有一些直观的意义。不过,我不知道还可以将侦听器附加到哪里,因为页面元素在 运行 时可能为空,也许这甚至不是问题所在。另一个区别是我认为它可能被声明为匿名函数,所以它不能通过 js 或其他东西获得 'garbage collected',如果那是一回事,所以我决定尽我所能将它作为一个命名函数。我仍然必须将命名函数包装在一个匿名函数中,我已经习惯了,因为否则我不知道如何将事件对象 e 传递给它,这是我需要的键码。但这并没有解决它。同样的问题。
function buttonListener(e: KeyboardEvent)
{
{
if (e.code == 'ArrowRight') { next() }
else if (e.code == 'ArrowLeft' && currentStep > 0) { back() }
}
}
document.body.addEventListener('keydown', (e) => { buttonListener(e) })
有人有什么想法吗? React 认为需要更新,但不是前几次。有什么变化?为什么前几次没有颠簸?我正在尝试更多地了解这两个事件和反应,所以这是一个有用的问题。
您应该只添加一次事件侦听器,并在模式消失后删除侦听器。
为此,我们可以在使用 useEffect
挂钩挂载 React 组件后添加监听器。如果你在 useEffect hook 之外添加监听器,它最终会在每次渲染时添加匿名事件监听器。
const keyboardEventListener = (e) =>
{
if (e.code == 'ArrowRight') next()
if (e.code == 'ArrowLeft' && currentStep > 0) back()
}
useEffect(() =>
{
document.body.addEventListener('keydown', keyboardEventListener);
return () =>
{
document.body.removeEventListener('keydown', keyboardEventListener);
}
}, [])
我正在 运行 在本地创建一个站点,并在 React 框架中部署了一个 80 页的模态。我使用状态通过 [currentStep, setCurrentStep]
挂钩在模态页面中移动。 setCurrentStep 可以通过三种方式调用——通过后退和下一步单击按钮,通过输入页码然后回车的输入框,以及通过左右键盘键。只有左右键给我一个问题。多次单击它们(中间有停顿)后,页面变得无响应。
我想这可能与监听器、阻塞或太多未完成的调用有关,我想我也许可以通过内存和事件监听器等开发工具的功能找到答案,但这些对我没有帮助。我注意到输入框选项也使用了类似的事件侦听器,所以我认为可能会有某种交互,所以我将其更改为 keyup 侦听器并且仍然具有相同的效果。
然后我注意到,当我单击一个箭头时,DOM 开始抖动:在两三页之间来回安装和卸载一些同级元素,尽管速度很快,但我还是可以挑出来。从一开始就这样做,我第一次注意到,这是正常的。第二次它只打了几下,然后就越来越高,几乎就像是一个 n!功能。
有趣的是,其他两个方法(按钮和输入)调用完全相同的 setCurrentStep,仅此而已。一定有一些副作用发生或某处有一些差异,这让人认为需要安装和卸载,尽管它们经过相同的狭窄路径。我肯定错过了什么。我很确定这不是数据类型问题。 next 和 back 函数处理递增和递减。
import React, { ReactNode, useState } from "react";
import { QuestionsObject } from './QuestionData/raw/QuestionCompiler'
import { ViewContainer } from './Views/ViewContainer'
const MultiStepAssessmentForm = (props) =>
{
const [currentStep, setCurrentStep] = useState(0);
let questionsArray = Object.keys(QuestionsObject)
let qIndex = questionsArray[currentStep]
const handleKeyDown = function (e)
{
if (e.code == 'NumpadEnter'){
setCurrentStep(parseInt(e.target.value))
e.target.value = ''
}
}
const next = () => { setCurrentStep(currentStep + 1) };
const back = () => { setCurrentStep(currentStep - 1); };
document.body.addEventListener('keydown', (e) =>
{
if (e.code == 'ArrowRight') next()
if (e.code == 'ArrowLeft' && currentStep > 0) back()
})
const formProps = {
data: formData,
handleInputBoxChange: handleInputBoxChange,
handleKeyValueCHange: handleKeyValueChange,
currentStep: currentStep,
next: next,
back: back,
}
function renderQuestions()
{
return (
<div className="temp Dev Div">
<input type="text" placeholder="Go to page X" onKeyDown={(e) => { handleKeyDown(e) }} />
<ViewContainer formProps={formProps} question={QuestionsObject[qIndex]} />
</div>
)
}
return (
<> {renderQuestions()}</>
)
};
export default MultiStepAssessmentForm;
我最好的猜测是,这是因为我将侦听器附加到主体上,这与 React 混淆了。这是我看到的两个差异之一,也有一些直观的意义。不过,我不知道还可以将侦听器附加到哪里,因为页面元素在 运行 时可能为空,也许这甚至不是问题所在。另一个区别是我认为它可能被声明为匿名函数,所以它不能通过 js 或其他东西获得 'garbage collected',如果那是一回事,所以我决定尽我所能将它作为一个命名函数。我仍然必须将命名函数包装在一个匿名函数中,我已经习惯了,因为否则我不知道如何将事件对象 e 传递给它,这是我需要的键码。但这并没有解决它。同样的问题。
function buttonListener(e: KeyboardEvent)
{
{
if (e.code == 'ArrowRight') { next() }
else if (e.code == 'ArrowLeft' && currentStep > 0) { back() }
}
}
document.body.addEventListener('keydown', (e) => { buttonListener(e) })
有人有什么想法吗? React 认为需要更新,但不是前几次。有什么变化?为什么前几次没有颠簸?我正在尝试更多地了解这两个事件和反应,所以这是一个有用的问题。
您应该只添加一次事件侦听器,并在模式消失后删除侦听器。
为此,我们可以在使用 useEffect
挂钩挂载 React 组件后添加监听器。如果你在 useEffect hook 之外添加监听器,它最终会在每次渲染时添加匿名事件监听器。
const keyboardEventListener = (e) =>
{
if (e.code == 'ArrowRight') next()
if (e.code == 'ArrowLeft' && currentStep > 0) back()
}
useEffect(() =>
{
document.body.addEventListener('keydown', keyboardEventListener);
return () =>
{
document.body.removeEventListener('keydown', keyboardEventListener);
}
}, [])