我遇到的问题是,当我设置事件监听器时,事件监听器看到的值不会随状态更新。就像它绑定到初始状态一样。
正确的方法是什么?
简单的示例:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const App = () => {
const [name, setName] = useState("Colin");
const [nameFromEventHandler, setNameFromEventHandler] = useState("");
useEffect(() => {
document.getElementById("name").addEventListener("click", handleClick);
}, []);
const handleButton = () => {
setName("Ricardo");
};
const handleClick = () => {
setNameFromEventHandler(name);
};
return (
<React.Fragment>
<h1 id="name">name: {name}</h1>
<h2>name when clicked: {nameFromEventHandler}</h2>
<button onClick={handleButton}>change name</button>
</React.Fragment>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
以下是Gif,由于某种原因SO代码段无效。
答案 0 :(得分:3)
因此,您的问题是您将空数组作为效果的第二个参数传递,因此效果永远不会被清除并再次触发。这意味着handleClick
只会在默认状态下关闭。您实际上已经写了:setNameFromEventHandler("Colin");
,涵盖了该组件的整个生命周期。
请尝试一起删除第二个参数,以便在状态更改时清除并触发效果。当效果重新触发时,将处理click事件的函数将在您的状态的最新版本中关闭。另外,从您的useEffect
返回一个函数,该函数将删除您的事件监听器。
例如
useEffect(() => {
document.getElementById("name").addEventListener("click", handleClick);
return () => {
document.getElementById("name").removeEventListener("click", handleClick);
}
});
答案 1 :(得分:0)
我认为正确的解决方案应该是:codesanbox。我们正在告诉效果要注意其依赖关系,即回调。每当更改它时,我们都应该在闭包中使用正确的值进行另一个绑定。
答案 2 :(得分:0)
我相信正确的解决方案是这样的:
useEffect(() => {
document.getElementById("name").addEventListener("click", handleClick);
}, [handleClick]);
const handleButton = () => {
setName("Ricardo");
};
const handleClick = useCallback(() => {
setNameFromEventHandler(name)
}, [name])
useEffect应该将handleClick作为其依赖项数组的一部分,否则它将遭受所谓的“过时的关闭”,即具有过时的状态。
要确保useEffect不在每个渲染上运行,请将handleClick移到useCallback内。这将返回一个已记忆的回调版本,仅当其中一个依赖项已更改(在这种情况下为“名称”)更改时,该更改才会更改。