使用 Hooks 在 React 中设置点击事件的时间间隔不会使用更新的状态值
Setting an interval on a click event in React using Hooks won't use updated state values
我正在尝试创建一个过滤器下拉菜单,如果用户没有手动关闭它并且在 1 秒后将鼠标从过滤器上移开,它会自行隐藏。
问题是 mouseInside
布尔状态永远不会在区间内更改为 false
。我相信这是因为反应组件被重新渲染但不完全确定。
我不知道如何修复它或我可能如何错误地使用状态变量与局部变量。
我在带有钩子的 React 中使用 TypeScript:
const Filter: React.FC = () => {
const [shown, setShown] = useState<boolean>(false);
const [mouseInside, setMouseInside] = useState<boolean>(false);
let interval: NodeJS.Timeout | null = null;
const handleToggle = () => {
setShown(!shown);
interval = setInterval(() => {
console.log(mouseInside);
// mouseInside is permanently true
if (!mouseInside && interval) {
// this never gets triggered
clearInterval(interval);
setShown(false);
console.log("Finished!");
}
}, 1000);
}
};
return (
<div
style={{ width: 100, height: 100, backgroundColor: "lightgray" }}
onClick={handleToggle}
onMouseEnter={() => setMouseInside(true)}
onMouseLeave={() => setMouseInside(false)}
>
<p>Click me!</p>
{shown && <div>drop down here...</dv>}
</div>
);
};
我需要 mouseInside 在用户离开过滤框时最终变为 false。 onMouseLeave
事件肯定会被触发,但我认为这是导致问题的 React 方面。
也许我需要使用 useEffect
?我不知道如何将其与 setInterval
.
一起使用的最佳方法
为了“订阅”状态变化的回调函数,您绝对可以使用 useEffect
钩子:
function automaticallySetMouseOutside() {
setTimeout(() => {
if (mouseInside) {
setMouseInside(false);
}
}, 1000);
}
useEffect(automaticallySetMouseOutside, [mouseInside]);
useEffect
的第二个参数订阅 automaticallySetMouseOutside
以便在 mouseInside
发生变化时被调用。
虽然此代码旨在举例说明此挂钩的用法,但它并不准确 - 因为它可以在用户仍在菜单中时关闭菜单。
如果您想完整解决您的问题,请查看此 CodeSandBox。
虽然在 handleToggle
内声明间隔操作,但它存在于自己的范围内,与实际状态没有绑定,
const handleInterval = interval => {
console.log(mouseInside);
if (!mouseInside && interval) {
// this never gets triggered
clearInterval(interval);
setShown(false);
console.log("Finished!");
}
}
const handleToggle = () => {
interval = setInterval(() => {
() => handleInterval(interval),
}, 1000);
}
};
我最终使用了@GalAbra 建议的部分内容,但我只想在用户单击切换器时创建间隔,并且仅在菜单打开时创建:
const [shown, setShown] = useState<boolean>(false);
const [mouseInside, setMouseInside] = useState<boolean>(false);
useEffect(() => {
let interval: NodeJS.Timeout | null;
if (shown) {
interval = setInterval(() => {
if (!mouseInside && interval) {
setShown(false);
}
}, 500);
}
return () => {
if (interval) clearInterval(interval);
};
}, [shown, mouseInside]);
return (
<div
style={{ width: 100, height: 100, backgroundColor: "lightgray" }}
onClick={() => setShown(!shown)}
onMouseEnter={() => setMouseInside(true)}
onMouseLeave={() => setMouseInside(false)}
>
<p>Click me!</p>
{shown && <div>drop down here...</dv>}
</div>
);
我正在尝试创建一个过滤器下拉菜单,如果用户没有手动关闭它并且在 1 秒后将鼠标从过滤器上移开,它会自行隐藏。
问题是 mouseInside
布尔状态永远不会在区间内更改为 false
。我相信这是因为反应组件被重新渲染但不完全确定。
我不知道如何修复它或我可能如何错误地使用状态变量与局部变量。
我在带有钩子的 React 中使用 TypeScript:
const Filter: React.FC = () => {
const [shown, setShown] = useState<boolean>(false);
const [mouseInside, setMouseInside] = useState<boolean>(false);
let interval: NodeJS.Timeout | null = null;
const handleToggle = () => {
setShown(!shown);
interval = setInterval(() => {
console.log(mouseInside);
// mouseInside is permanently true
if (!mouseInside && interval) {
// this never gets triggered
clearInterval(interval);
setShown(false);
console.log("Finished!");
}
}, 1000);
}
};
return (
<div
style={{ width: 100, height: 100, backgroundColor: "lightgray" }}
onClick={handleToggle}
onMouseEnter={() => setMouseInside(true)}
onMouseLeave={() => setMouseInside(false)}
>
<p>Click me!</p>
{shown && <div>drop down here...</dv>}
</div>
);
};
我需要 mouseInside 在用户离开过滤框时最终变为 false。 onMouseLeave
事件肯定会被触发,但我认为这是导致问题的 React 方面。
也许我需要使用 useEffect
?我不知道如何将其与 setInterval
.
为了“订阅”状态变化的回调函数,您绝对可以使用 useEffect
钩子:
function automaticallySetMouseOutside() {
setTimeout(() => {
if (mouseInside) {
setMouseInside(false);
}
}, 1000);
}
useEffect(automaticallySetMouseOutside, [mouseInside]);
useEffect
的第二个参数订阅 automaticallySetMouseOutside
以便在 mouseInside
发生变化时被调用。
虽然此代码旨在举例说明此挂钩的用法,但它并不准确 - 因为它可以在用户仍在菜单中时关闭菜单。
如果您想完整解决您的问题,请查看此 CodeSandBox。
虽然在 handleToggle
内声明间隔操作,但它存在于自己的范围内,与实际状态没有绑定,
const handleInterval = interval => {
console.log(mouseInside);
if (!mouseInside && interval) {
// this never gets triggered
clearInterval(interval);
setShown(false);
console.log("Finished!");
}
}
const handleToggle = () => {
interval = setInterval(() => {
() => handleInterval(interval),
}, 1000);
}
};
我最终使用了@GalAbra 建议的部分内容,但我只想在用户单击切换器时创建间隔,并且仅在菜单打开时创建:
const [shown, setShown] = useState<boolean>(false);
const [mouseInside, setMouseInside] = useState<boolean>(false);
useEffect(() => {
let interval: NodeJS.Timeout | null;
if (shown) {
interval = setInterval(() => {
if (!mouseInside && interval) {
setShown(false);
}
}, 500);
}
return () => {
if (interval) clearInterval(interval);
};
}, [shown, mouseInside]);
return (
<div
style={{ width: 100, height: 100, backgroundColor: "lightgray" }}
onClick={() => setShown(!shown)}
onMouseEnter={() => setMouseInside(true)}
onMouseLeave={() => setMouseInside(false)}
>
<p>Click me!</p>
{shown && <div>drop down here...</dv>}
</div>
);