为什么我无法清除我的 setInterval?
Why I'm not able to clear my setInterval?
我正在尝试创建一个 自动计数器 当组件(例如主页)在视图中呈现时。计数器应从 1 开始并在 10 停止。但我无法在 10 时清除我的 setInterval。计数器保持递增。
有什么问题?
非常感谢。
export const Home = () => {
const [counter, setCounter] = useState(1)
let timer = useRef()
const animate = () => {
if(counter === 10){
clearInterval(timer.current)
}else{
setCounter((previous) => previous + 1)
}
}
useEffect(() => {
timer.current = window.setInterval(() => {
animate()
}, 900)
}, [])
return (
<div style={{textAlign: "center"}}>
<h1>{counter}</h1>
</div>
)
}
问题是 animate
函数在 counter
的初始值上的闭包,即 1。结果,条件 counter === 10
永远不会计算为真。
这就是为什么你永远不应该在 useEffect
钩子的依赖性问题上撒谎。这样做可能会导致错误,因为关闭了组件先前渲染中的陈旧值。
解决方案
您可以去掉 animate()
函数并在 useEffect
钩子中编写逻辑。
function App() {
const [counter, setCounter] = React.useState(1);
const counterRef = React.useRef(counter);
React.useEffect(() => {
const id = setInterval(() => {
// if "counter" is 10, stop the interval
if (counterRef.current === 10) {
clearInterval(id);
} else {
// update the "counter" and "counterRef"
setCounter((prevCounter) => {
counterRef.current = ++prevCounter;
return prevCounter;
});
}
}, 900);
// clearn interval on unmount
return () => clearInterval(id);
}, []);
return (
<div style={{ textAlign: "center" }}>
<h1>{counter}</h1>
</div>
);
}
ReactDOM.render(<App/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
那是因为修改 ref.current
不会触发重新渲染。
您需要使用useEffect
清除它。
const animate = () => {
setCounter((previous) => previous + 1);
};
useEffect(() => {
if (counter === 10) clearInterval(timer.current);
}, [counter]);
我会创建第二个 useEffect()
来清除间隔:
const { useEffect, useRef, useState } = React;
const Home = () => {
const [counter, setCounter] = useState(1);
const timer = useRef();
useEffect(() => {
timer.current = setInterval(() => {
setCounter(x => x + 1);
}, 900);
// in case the component unmounts before the timer finishes
return () => clearInterval(timer.current);
}, []);
useEffect(() => {
if (counter === 10) {
clearInterval(timer.current);
}
}, [counter]);
return (
<div style={{ textAlign: 'center' }}>
<h1>{counter}</h1>
</div>
);
}
ReactDOM.render(<Home/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
我正在尝试创建一个 自动计数器 当组件(例如主页)在视图中呈现时。计数器应从 1 开始并在 10 停止。但我无法在 10 时清除我的 setInterval。计数器保持递增。 有什么问题?
非常感谢。
export const Home = () => {
const [counter, setCounter] = useState(1)
let timer = useRef()
const animate = () => {
if(counter === 10){
clearInterval(timer.current)
}else{
setCounter((previous) => previous + 1)
}
}
useEffect(() => {
timer.current = window.setInterval(() => {
animate()
}, 900)
}, [])
return (
<div style={{textAlign: "center"}}>
<h1>{counter}</h1>
</div>
)
}
问题是 animate
函数在 counter
的初始值上的闭包,即 1。结果,条件 counter === 10
永远不会计算为真。
这就是为什么你永远不应该在 useEffect
钩子的依赖性问题上撒谎。这样做可能会导致错误,因为关闭了组件先前渲染中的陈旧值。
解决方案
您可以去掉 animate()
函数并在 useEffect
钩子中编写逻辑。
function App() {
const [counter, setCounter] = React.useState(1);
const counterRef = React.useRef(counter);
React.useEffect(() => {
const id = setInterval(() => {
// if "counter" is 10, stop the interval
if (counterRef.current === 10) {
clearInterval(id);
} else {
// update the "counter" and "counterRef"
setCounter((prevCounter) => {
counterRef.current = ++prevCounter;
return prevCounter;
});
}
}, 900);
// clearn interval on unmount
return () => clearInterval(id);
}, []);
return (
<div style={{ textAlign: "center" }}>
<h1>{counter}</h1>
</div>
);
}
ReactDOM.render(<App/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
那是因为修改 ref.current
不会触发重新渲染。
您需要使用useEffect
清除它。
const animate = () => {
setCounter((previous) => previous + 1);
};
useEffect(() => {
if (counter === 10) clearInterval(timer.current);
}, [counter]);
我会创建第二个 useEffect()
来清除间隔:
const { useEffect, useRef, useState } = React;
const Home = () => {
const [counter, setCounter] = useState(1);
const timer = useRef();
useEffect(() => {
timer.current = setInterval(() => {
setCounter(x => x + 1);
}, 900);
// in case the component unmounts before the timer finishes
return () => clearInterval(timer.current);
}, []);
useEffect(() => {
if (counter === 10) {
clearInterval(timer.current);
}
}, [counter]);
return (
<div style={{ textAlign: 'center' }}>
<h1>{counter}</h1>
</div>
);
}
ReactDOM.render(<Home/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>