我正在关注Dan Abramov的这篇文章:
https://overreacted.io/making-setinterval-declarative-with-react-hooks/
在文章中,Dan 做了一个自定义的 useInterval 钩子,以创建一个动态的 setInterval。
钩子看起来像这样:
export default function useInterval(callback, delay) {
//this useInterval function will be called whenever the parent component renders.
// on render, savedCallback.current gets set to whatever the callback is, if the callback
// has changed
const savedCallback = useRef();
console.log("called")
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
/**
* Likewise, the set interval is set off,
* and if delay is
*/
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => {
console.log("clearEed!")
clearInterval(id);
}
}
}, [delay]);
}
不过,有一部分我不明白,就在这里:
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => {
console.log("clearEed!")
clearInterval(id);
}
}
}, [delay]);
我知道如果延迟更改,则会调用此 useEffect。回调被分配给tick,然后如果延迟不为空,id设置为SetInterval,并将tick和延迟作为参数。这一切都是有道理的。但接下来发生的事情对我来说很奇怪。我知道 useEffect 可以在组件卸载时采用 return 语句,但为什么要清除之前设置的间隔?如果有人能告诉我这个问题,我将不胜感激。
特别是,我真的很想帮助理解这些行:
if (delay !== null) {
let id = setInterval(tick, delay);
return () => {
console.log("clearEed!")
clearInterval(id);
}
}
我是这样使用它的:
function TimerWithHooks() {
let [count, setCount] = useState(0);
let [delay, setDelay] = useState(1000);
useInterval(() => {
setCount(count + 1);
}, delay)
const handleDelayChange = evt => {
setDelay(Number(evt.target.value))
}
return (
<>
<h1>{count}</h1>
<input value={delay} onChange={handleDelayChange} />
</>
);
}
export default TimerWithHooks;
清理效果
React 究竟什么时候清理效果?React 在组件卸载时执行清理。然而,正如我们 前面学习过,效果针对每个渲染运行,而不仅仅是一次。这就是为什么 React 还会在下次运行效果之前清理上一个渲染的效果。我们将讨论为什么这有助于避免错误 以及如何选择退出此行为,以防它产生性能 下面稍后的问题。
这意味着每次delay
更改时,效果都会清除以前的效果,因此每次我们更改延迟时它都会清除计时器,而不是在组件卸载时清除计时器。这样,我们可以动态调整计时器,而不必担心清除计时器。
我猜 Dan 会在组件卸载时清除计时器,但我认为 beater 在执行函数后会这样做。 有人舔这个:
useEffect(() => {
if (delay !== null) {
let timerId = setInterval(
() => {
savedCallback.current();
clearInterval(timerId);
},
delay
);
}
}, [delay]);