我使用函数组件是因为我知道这是现代的方式。我想定义一个访问组件状态的键处理程序。直观优雅的方法行不通。
import React, { useState, useEffect } from "react"
我尝试了3种方法:
方法1
export default function CounterApp() {
const [counter, setCounter] = useState(0);
useEffect(() => document.addEventListener(
'keydown',
(e) => {
console.log("Got keystroke", e.key, counter);
if (e.key === "1") {
setCounter(counter + 1);
}
}
), []);
console.log("render");
return <div>The counter is: {counter}</div>
}
方法二:
export default function CounterApp() {
const [counter, setCounter] = useState(0);
useEffect(
() => document.addEventListener('keydown', keyhandler),
[]
);
function keyhandler(e) {
console.log("Got keystroke", e.key, counter);
if (e.key === "1") {
setCounter(counter + 1);
}
}
console.log("render");
return <div>The counter is: {counter}</div>
}
方法三:
export default function CounterApp() {
const [counter, setCounter] = useState(0);
useEffect(() => {
document.addEventListener('keydown', keyhandler);
return () => document.removeEventListener('keydown', keyhandler);
}, [counter]);
function keyhandler(e) {
console.log("Got keystroke", e.key, counter);
if (e.key === "1") {
setCounter(counter + 1);
}
}
console.log("render");
return <div>The counter is: {counter}</div>
}
方法3确实有效,但似乎需要大量打字,可能效率低下(频繁调用
useEffect
),而且容易出错,所以我想知道为什么更直接的方法不起作用。我更愿意在组件的生命周期中仅添加一次事件侦听器,添加一个永远不会更改的函数(因此无需将其添加为 useEffect
依赖项),尽管该函数引用了一个确实会更改的状态变量。 React 是否真的需要在状态变量输入发生变化时重新解析函数定义?第三种方法是否正确且最佳?前两种方法有什么问题吗?
方法3很接近(它可以工作,但并不理想,就像你说的那样),但是你不应该在效果之外定义
keyhandler
,你应该在效果内部定义它。
您的主要问题(在方法 2 中也是如此,该方法几乎有效,但仍然不理想,因为
useEffect
中缺少返回函数)是您缺少状态更新器作为函数,而是捕获了counter
当您的事件侦听器被附加时(第一次渲染时它始终为 0,并且每次按下某个键时 counter
仍将是 0)。
这个:
setCounter(c => c+1)
而不是:
setCounter(counter+1)
如果使用函数更新状态,则只需在安装时应用效果,而不是在计数器更改时应用效果:
export default function CounterApp() {
const [counter,setCounter] = useState(0);
useEffect(() => {
const keyhandler = (e) => {
if(e.key === "1") setCounter(c => c+1)
}
document.addEventListener('keydown', keyhandler);
return () => document.removeEventListener('keydown', keyhandler);
},[]);
console.log("render");
return <div>The counter is: {counter} </div>
}