使用useEffect,你“告诉 React 你的组件需要在渲染后做一些事情”(ref)。我在您的代码中添加了一些额外的日志记录,以获取时间:
function time(msg) {
return '[' + new Date().getTime() + '] ' + msg
}
function Delayed({ children, wait = 500 }) {
const [show, setShow] = React.useState(false);
React.useEffect(() => {
console.log(time("effect"));
const timeout = window.setTimeout(() => {
console.log(time("setTimeout"), timeout, show);
setShow(true);
}, wait);
console.log(time("created timeout"), timeout);
return () => {
console.log(time("cleanup"), timeout);
window.clearTimeout(timeout);
};
});
console.log(time("render"), show);
return show === true ? children : null;
}
它输出:
[1641339330296] render false
[1641339330315] effect
[1641339330315] created timeout 16
[1641339330816] setTimeout 16 false
[1641339330818] render true
[1641339330824] cleanup 16
[1641339330825] effect
[1641339330825] created timeout 24
[1641339331325] setTimeout 24 true
[1641339331327] render true
因此,效果的超时按预期在第一个render false 之后触发,但它在render true 之后再次触发,并设置另一个超时,这是不需要的。您可以使用if (!show) 来简单地防范这种情况,如下所示:
function Delayed({ children, wait = 500 }) {
const [show, setShow] = React.useState(false);
React.useEffect(() => {
console.log(time("effect"));
if (!show) { // <--- THE CHANGE IS HERE
const timeout = window.setTimeout(() => {
console.log(time("setTimeout"), timeout, show);
setShow(true);
}, wait);
console.log(time("created timeout"), timeout);
return () => {
console.log(time("cleanup"), timeout);
window.clearTimeout(timeout);
};
}
});
console.log(time("render"), show);
return show === true ? children : null;
}
输出:
[1641339821991] render false
[1641339822010] effect
[1641339822010] created timeout 16
[1641339822510] setTimeout 16 false
[1641339822513] render true
[1641339822517] cleanup 16
[1641339822518] effect