一开始,理解React钩子可能会让人望而生畏,特别是当您遇到与时间相关的任何事情时,比如setInterval()。为了解决这些问题,您必须习惯hook的工作方式、它们的限制和潜在的变通方法。
首先,应该清楚setInterval()是一个副作用。毕竟,它没有直接绑定到组件的渲染方法。因此,我们应该在useEffect()钩子中调用它,并在卸载时使用它的返回值调用clearInterval()。为了避免创建多个间隔,可以使用钩子的第二个参数传递一个空的依赖数组([])。这导致只有在组件挂载时才会运行副作用。
React.useEffect(() => {
let id = setInterval(callback, delay);
return () => clearInterval(id);
}, []);
setInterval()内部的闭包只会在实例化时访问任何可用的变量和值。这意味着我们必须特别小心它的第一个参数,以确保每次interval运行时都有新的值可用。这个问题最简单的解决方案是使用useRef()钩子创建一个被React认为是可变的变量。这将允许我们在需要时访问新值
const savedCallback = React.useRef(callback);
React.useEffect(() => {
let id = setInterval(savedCallback.current, delay);
return () => clearInterval(id);
}, []);
使用useRef()钩子可能只是转移了问题。创建的引用的值现在需要在setInterval()中刷新。幸运的是,这是一个容易解决的问题。我们可以创建一个包装器函数,将该函数传递给setInterval()。通过这种方式,传递给setInterval()的函数将永远不会改变,但被括起来的引用的值在被调用时始终是最新的
const savedCallback = React.useRef(callback);
React.useEffect(() => {
function tick() {
savedCallback.current();
}
let id = setInterval(tick, delay);
return () => clearInterval(id);
}, []);
最后,让我们将所有这些提取到一个自定义钩子中,以使其可重用。我们可以提取callback作为自定义钩子的参数,并将它用作附加的useEffect()钩子的唯一依赖项,该钩子将更新回调的引用。
const useInterval = (callback, delay) => {
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]);
};
差不多就是这样。只要稍加努力,我们就可以为自定义钩子的参数添加延迟,并拥有一个完整的钩子版本的setInterval()。您可以找到这个钩子的最后调整的实现,以及一些用法示例
const Timer = props => {
const [seconds, setSeconds] = React.useState(0);
useInterval(() => {
setSeconds(seconds + 1);
}, 1000);
return <p>{seconds}</p>;
};
ReactDOM.render(<Timer />, document.getElementById('root'));