我正在尝试新的React钩子,并有一个时钟组件,它的时间值应该每秒钟增加。但是,该值不会超过1。
function Clock() {
const [time, setTime] = React.useState(0);
React.useEffect(() => {
const timer = window.setInterval(() => {
setTime(time + 1);
}, 1000);
return () => {
window.clearInterval(timer);
};
}, []);
return (
<div>Seconds: {time}</div>
);
}
ReactDOM.render(<Clock />, document.querySelector('#app'));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>
原因是传递给setInterval闭包的回调只访问第一次呈现中的时间变量,它不能访问后续呈现中的新时间值,因为第二次没有调用useEffect()。
在setInterval回调函数中,time的值总是0。
与您熟悉的setState一样,状态钩子有两种形式:一种是它接收更新状态的形式,另一种是传递当前状态的回调形式。您应该使用第二种形式,并在setState回调中读取最新的状态值,以确保在增加状态值之前拥有最新的状态值。
好处:可选择的方法
Dan Abramov在他的博客文章中深入讨论了关于使用setInterval和钩子的话题,并提供了解决这个问题的替代方法。强烈推荐阅读!
function Clock() {
const [time, setTime] = React.useState(0);
React.useEffect(() => {
const timer = window.setInterval(() => {
setTime(prevTime => prevTime + 1); // <-- Change this line!
}, 1000);
return () => {
window.clearInterval(timer);
};
}, []);
return (
<div>Seconds: {time}</div>
);
}
ReactDOM.render(<Clock />, document.querySelector('#app'));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>
当提供空输入列表时,useEffect函数只在组件挂载时计算一次。
setInterval的另一个替代方法是在每次状态更新时使用setTimeout设置新的间隔时间:
const [time, setTime] = React.useState(0);
React.useEffect(() => {
const timer = setTimeout(() => {
setTime(time + 1);
}, 1000);
return () => {
clearTimeout(timer);
};
}, [time]);
setTimeout对性能的影响很小,一般可以忽略。除非组件对时间敏感,以至于新设置的超时会导致不良影响,否则setInterval和setTimeout方法都是可以接受的。