在React中编写useInterval钩子
理解React hooks可能一开始会让人望而生畏,特别是如果你遇到与时间相关的问题,比如setInterval()
。为了解决这类问题,你必须熟悉hooks的工作方式、它们的限制以及潜在的解决方法。
首先,应该明确setInterval()
是一个副作用。毕竟,它不直接与组件的渲染方法相关联。因此,我们应该在useEffect()
钩子内调用它,并使用其return
来在卸载时调用clearInterval()
。为了避免创建多个间隔,我们可以使用钩子的第二个参数来传递一个空的依赖数组([]
)。这样只有在组件挂载时才会运行副作用。
React.useEffect(() => {
let id = setInterval(callback, delay);
return () => clearInterval(id);
}, []);
setInterval()
内部的闭包只能访问在其实例化时可用的变量和值。这意味着我们必须特别注意其第一个参数,以确保每次间隔运行时都有新的值可用。解决这个问题的最简单方法是创建一个被React视为可变的变量,使用useRef()
钩子。这样我们就可以在需要时访问新的值。
const savedCallback = React.useRef(callback);
React.useEffect(() => {
let id = setInterval(savedCallback.current, delay);
return () => clearInterval(id);
}, []);
使用useRef()
钩子可能只是转移了问题。现在,创建的ref的值需要在setInterval()
内部刷新。幸运的是,这是一个容易解决的问题。我们可以创建一个包装函数,将函数传递给setInterval()
。这样,传递给setInterval()
的函数将永远不会改变,但封闭的ref的值在调用时将始终是最新的。
const savedCallback = React.useRef(callback);
React.useEffect(() => {
function tick() {
savedCallback.current();
}
let id = setInterval(tick, delay);
return () => clearInterval(id);
}, []);
最后,让我们将所有内容提取到一个自定义的hook中,以便重用。我们可以将callback
作为自定义hook的参数提取出来,并将其作为额外的useEffect()
hook的唯一依赖项,用于更新回调函数的ref。
const useInterval = callback => {
const savedCallback = React.useRef();
React.useEffect(() => {
savedCallback.current = callback;
}, [callback]);
React.useEffect(() => {
function tick() {
savedCallback.current();
}
let id = setInterval(tick, delay);
return () => clearInterval(id);
}, []);
};
就是这样了。稍加努力,我们可以将delay
添加到自定义hook的参数中,并得到一个完整的setInterval()
的hook版本。你可以在useInterval代码片段中找到这个最终调整的hook的实现,以及一些使用示例。