React中let函数作用域为何无法保存setInterval ID?求解析
问题:React中let声明的定时器ID在暂停时为何变成undefined?
以下是我的代码:
import "./styles.css"; import { useEffect, useReducer, useState } from "react"; export default function App() { const [counter, setCounter] = useState(0); let intervalId; const handleCounter = () => { intervalId = setInterval(() => { setCounter((prevState) => prevState + 1); }, 1000); console.log("intervalId in counter:", intervalId); }; const handlePause = () => { console.log("intervalId:", intervalId); clearInterval(intervalId); }; return ( <div className="App"> <h1>Counter-{counter}</h1> <button onClick={handleCounter}>Start</button> <button onClick={handlePause}>Pause</button> </div> ); }
我先点击Start按钮触发handleCounter函数,尝试将setInterval返回的ID存入let声明的intervalId变量,随后点击Pause按钮触发handlePause尝试清除定时器时,控制台输出intervalId为undefined,计数器无法停止。
备注:我知道将ID存入state可以解决该问题,但我更想了解为何使用let函数作用域在此场景下无法生效?
解答
核心问题出在React的组件渲染机制上:
- 组件中的
let intervalId属于当前渲染周期的局部变量,每次组件状态更新(比如调用setCounter改变计数器值),都会触发组件重新执行整个App函数,这时候会重新声明一个全新的intervalId变量,和之前存储定时器ID的那个变量完全是两个独立的东西。 - 当你点击Start时,定时器ID存在了当前渲染周期的
intervalId里,但紧接着setCounter触发组件重新渲染,新的渲染周期会生成新的intervalId(未赋值),同时也会生成新的handlePause函数。 - 你点击Pause时,调用的是重新渲染后生成的
handlePause,它访问的是当前渲染周期里未赋值的intervalId,自然就是undefined,也就无法清除之前的定时器。
而把ID存入state时,state是React专门用来跨渲染周期保存状态的容器,不管组件渲染多少次,state里的值都会被稳定保留,所以能拿到正确的定时器ID来清除定时器。
内容的提问来源于stack exchange,提问作者Abhishek Kumar Singh
相关产品推荐
相关产品推荐

