在状态更新时使用 eventListener 进行指数重新渲染
Exponential re-rendering with eventListener on state update
我正在尝试在“keydown”上使用 eventListener
来更新我的状态(一个名为 showMenu
的布尔值)。
我已将它放在 useEffect
上,但它无法正常工作,我不明白为什么...
如果我在 useEffect 的末尾将 showMenu
放在数组上,它会以指数方式重新呈现组件。
如果我不把 showMenu
放在 useEffect
的末尾,那么 showMenu
只会更新一次(从 false 到 true)
这里是指数级重新渲染的代码
const [showMenu, setShowMenu] = useState(false);
useEffect(() => {
window.addEventListener("keydown", (e) => handleMenu(e));
return () => {
window.removeEventListener("keydown", (e) => handleMenu(e));
};
}, [showMenu]);
const handleMenu = (e) => {
if (e.key == "m") {
setShowMenu(!showMenu);
}
};
removeEventListener
接受一个函数 reference 但你每次都在创建一个匿名函数:
useEffect(() => {
window.addEventListener("keydown", handleMenu);
return () => {
window.removeEventListener("keydown", handleMenu);
};
}, [showMenu]);
通过删除匿名函数包装器(又名减少 eta),addEventListener 和 removeEventListener 获得相同的函数引用。
或者,如果您只支持现代浏览器并且想忽略函数引用的概念,addEventListener 支持 AbortSignal
s:
useEffect(() => {
const ac = new AbortController();
window.addEventListener("keydown", (e) => handleMenu(e), { signal: ac.signal });
return () => ac.abort();
}, [showMenu]);
附带说明一下,确实没有理由将 showMenu
传递到函数的依赖列表中,我不会传递它。
好的,刚想通了。
useEffect 方法不是问题所在。逻辑很好。
问题出在我需要直接更改为 handleMenu
的 (e) => handleMenu(e)
上。
见下文:
const [showMenu, setShowMenu] = useState(false);
const handleMenu = (e) => {
if (e.key == "m") {
setShowMenu(!showMenu);
}
};
useEffect(() => {
window.addEventListener("keydown", handleMenu);
return () => {
window.removeEventListener("keydown", handleMenu);
};
}, [showMenu]);
感谢 Benjamin 给我答案:我需要传递我的 handleMenu 函数引用而不是使用 (e) =>
语法,它每次都根据我的 handleMenu 函数创建一个新函数,它解释了指数渲染: )
我正在尝试在“keydown”上使用 eventListener
来更新我的状态(一个名为 showMenu
的布尔值)。
我已将它放在 useEffect
上,但它无法正常工作,我不明白为什么...
如果我在 useEffect 的末尾将 showMenu
放在数组上,它会以指数方式重新呈现组件。
如果我不把 showMenu
放在 useEffect
的末尾,那么 showMenu
只会更新一次(从 false 到 true)
这里是指数级重新渲染的代码
const [showMenu, setShowMenu] = useState(false);
useEffect(() => {
window.addEventListener("keydown", (e) => handleMenu(e));
return () => {
window.removeEventListener("keydown", (e) => handleMenu(e));
};
}, [showMenu]);
const handleMenu = (e) => {
if (e.key == "m") {
setShowMenu(!showMenu);
}
};
removeEventListener
接受一个函数 reference 但你每次都在创建一个匿名函数:
useEffect(() => {
window.addEventListener("keydown", handleMenu);
return () => {
window.removeEventListener("keydown", handleMenu);
};
}, [showMenu]);
通过删除匿名函数包装器(又名减少 eta),addEventListener 和 removeEventListener 获得相同的函数引用。
或者,如果您只支持现代浏览器并且想忽略函数引用的概念,addEventListener 支持 AbortSignal
s:
useEffect(() => {
const ac = new AbortController();
window.addEventListener("keydown", (e) => handleMenu(e), { signal: ac.signal });
return () => ac.abort();
}, [showMenu]);
附带说明一下,确实没有理由将 showMenu
传递到函数的依赖列表中,我不会传递它。
好的,刚想通了。
useEffect 方法不是问题所在。逻辑很好。
问题出在我需要直接更改为 handleMenu
的 (e) => handleMenu(e)
上。
见下文:
const [showMenu, setShowMenu] = useState(false);
const handleMenu = (e) => {
if (e.key == "m") {
setShowMenu(!showMenu);
}
};
useEffect(() => {
window.addEventListener("keydown", handleMenu);
return () => {
window.removeEventListener("keydown", handleMenu);
};
}, [showMenu]);
感谢 Benjamin 给我答案:我需要传递我的 handleMenu 函数引用而不是使用 (e) =>
语法,它每次都根据我的 handleMenu 函数创建一个新函数,它解释了指数渲染: )