受此post的启发,如果我们在延迟后在清理中删除事件侦听器,事件处理程序的先前引用是否仍会附加到浏览器?
这是代码:
import React from "react";
import "./style.css";
// import React from "react";
// import "./style.css";
import { useState, useEffect,useRef } from 'react';
export default function App() {
const [userText, setUserText] = useState('');
// Any event handler (function in JS) declared with (){} will be a new
// object in every render. So for every render hanleUserKeyPress is assigned a
// new reference
const handleUserKeyPress = event => {
const { key, keyCode } = event;
if (keyCode === 32 || (keyCode >= 65 && keyCode <= 90)) {
// When this function is added as event listener to any browser activity
// The entire code gets snapshotted i.e for every activity userText would
// remain same.
// For any browser activity added to this tag it will see :
// const handleUserKeyPress = event => {
// const { key, keyCode } = event;
// if (keyCode === 32 || (keyCode >= 65 && keyCode <= 90)) {
// console.log('Value of userText : ' + '')//Added as snapshot
// console.log('Value of userText + key : ' + ''+key)
// setUserText(''+key);
// }
// };
console.log('Value of userText : ' + userText)
console.log('Value of userText + key : ' + userText+key)
setUserText(userText+key);
}
};
// This runs on every render(no dependency declared).
useEffect(() => {
window.addEventListener('keydown', handleUserKeyPress);
return () => {
// Add a delay to see
async function fun(){
await delay();
window.removeEventListener('keydown', handleUserKeyPress);
}
fun();
};
});
return (
<div>
<h1>Feel free to type!</h1>
<blockquote>{userText}</blockquote>
</div>
);
}
function delay(){
return new Promise(resolve =>{
setTimeout(()=> resolve(),5000)
})
}
因此,当安装程序最初运行时,React 会在handleUserKeyPress 中拍摄 userText 状态的快照,并将其添加到 keydown 事件中。
当按下某个键(假设为“k”)时,将调用 setUserText,因此组件的渲染将以值(“+”k”=“k”)进行。将代码提交到浏览器 DOM 后,应该运行清理操作。但在这里,我提供了 5 秒的延迟。在此期间,如果我按任意键,先前版本的事件处理程序仍会添加到 keydown 事件中。因此,在 5 秒结束之前按下的任何键(假设“m”)都会触发 handleUserKeyPress 函数,其中 userText 为“”,因此控制台应该打印“m”而不是“km”。另外 userText 应设置为“m”而不是“km”。
我哪里理解错了?
您的代码每次组件渲染时都会创建一个新的“按键”事件侦听器,并且由于您延迟删除现有侦听器,这意味着在任何给定时刻都可以有超过 1 个按键事件侦听器处于活动状态并侦听。
在此期间,如果我按任意键,以前版本的 事件处理程序仍然添加到 keydown 事件中。因此任何 在 5 秒结束之前按下的键(假设为“m”)将 使用handleUserKeyPress
作为
触发userText
函数,因此 控制台应该打印''
而不是'm'
。另外'km'
应设置为userText
而不是'm'
。 这是我认为您应该看到的确切行为,但我认为您误解了控制台日志。以下是一些带注释的日志输出示例:'km'
"" + "k"
5秒内按下“m”
按下“m”,按键侦听器 #1 控制台记录输出 -> 每个状态更新都会覆盖之前的状态,因此您会错过 描述 | |
---|---|
组件安装并创建按键侦听器#1 | |
“k”被按下,按键侦听器 #1 控制台记录输出 | -> , 状态更新排队到 ,调用 挂钩清理函数并将超时排队,组件重新渲染,并且按键侦听器 #2 在闭包中添加了
|
"" + "m" | "m" , userText 状态更新排队到 "m" ,按键侦听器 #2 控制台记录输出 "k" + "m" -> "km" , userText 状态更新排队到 "km" ,调用 useEffect 钩子清理函数并将超时排队,组件重新渲染,并添加按键侦听器#3 |
"m"
"km"
覆盖,并且组件会使用当前状态值重新渲染。