Diğerlerinin de belirttiği gibi, sorun useStateyalnızca bir kez çağrılmasıdır (çünkü deps = []aralığı ayarlamak için:
React.useEffect(() => {
const timer = window.setInterval(() => {
setTime(time + 1);
}, 1000);
return () => window.clearInterval(timer);
}, []);
Daha sonra, her setIntervaltıklandığında, aslında çağıracak setTime(time + 1), ancak timeher zaman setIntervalgeri arama (kapanış) tanımlandığında başlangıçta sahip olduğu değeri tutacaktır .
useStateAyarlayıcısının alternatif biçimini kullanabilir ve ayarlamak istediğiniz gerçek değer yerine bir geri arama sağlayabilirsiniz (aynen olduğu gibi setState):
setTime(prevTime => prevTime + 1);
Ancak Dan Abramov'un burada React Hooks ile setInterval Bildirimi Yapma bölümünde önerdiği gibi, useIntervalkodunuzu setInterval bildirime dayalı olarak KURU ve basitleştirmek için kendi kancanızı oluşturmanızı tavsiye ederim :
function useInterval(callback, delay) {
const intervalRef = React.useRef();
const callbackRef = React.useRef(callback);
React.useEffect(() => {
callbackRef.current = callback;
}, [callback]);
React.useEffect(() => {
if (typeof delay === 'number') {
intervalRef.current = window.setInterval(() => callbackRef.current(), delay);
return () => window.clearInterval(intervalRef.current);
}
}, [delay]);
return intervalRef;
}
const Clock = () => {
const [time, setTime] = React.useState(0);
const [isPaused, setPaused] = React.useState(false);
const intervalRef = useInterval(() => {
if (time < 10) {
setTime(time + 1);
} else {
window.clearInterval(intervalRef.current);
}
}, isPaused ? null : 1000);
return (<React.Fragment>
<button onClick={ () => setPaused(prevIsPaused => !prevIsPaused) } disabled={ time === 10 }>
{ isPaused ? 'RESUME ⏳' : 'PAUSE 🚧' }
</button>
<p>{ time.toString().padStart(2, '0') }/10 sec.</p>
<p>setInterval { time === 10 ? 'stopped.' : 'running...' }</p>
</React.Fragment>);
}
ReactDOM.render(<Clock />, document.querySelector('#app'));
body,
button {
font-family: monospace;
}
body, p {
margin: 0;
}
p + p {
margin-top: 8px;
}
#app {
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
}
button {
margin: 32px 0;
padding: 8px;
border: 2px solid black;
background: transparent;
cursor: pointer;
border-radius: 2px;
}
<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>
Daha basit ve daha temiz bir kod üretmenin yanı sıra, bu, aralığı otomatik olarak geçerek duraklatmanıza (ve temizlemenize) olanak tanır delay = nullve ayrıca kendiniz manuel olarak iptal etmek istemeniz durumunda aralık kimliğini de döndürür (bu, Dan'in yayınlarında ele alınmamıştır).
Aslında, bu delay, duraklatılmadığında yeniden başlamaması için de geliştirilebilir, ancak çoğu kullanım durumu için bu yeterince iyi sanırım.
Eğer için de benzer bir cevap arıyorsanız setTimeoutziyade setInterval: Bu out https://stackoverflow.com/a/59274757/3723993 .
Ayrıca bildirimsel sürümünü bulabilirsiniz setTimeoutve setInterval, useTimeoutve useInterval, ve bir de özel useThrottledCallbackde daktilo yazılı kanca https://gist.github.com/Danziger/336e75b6675223ad805a88c2dfdcfd4a .