जैसा कि अन्य ने बताया है, समस्या यह है कि useState
केवल एक बार कहा जाता है (जैसा deps = []
कि अंतराल को स्थापित करने के लिए:
React.useEffect(() => {
const timer = window.setInterval(() => {
setTime(time + 1);
}, 1000);
return () => window.clearInterval(timer);
}, []);
फिर, हर बार setInterval
टिक करने के बाद, यह वास्तव में कॉल करेगा setTime(time + 1)
, लेकिन कॉलबैक (बंद) परिभाषित होने पर time
हमेशा शुरू में इसका मूल्य होगा setInterval
।
आप के useState
सेटर के वैकल्पिक रूप का उपयोग कर सकते हैं और जो वास्तविक मूल्य आप सेट करना चाहते हैं (केवल उसी तरह setState
) के बजाय कॉलबैक प्रदान कर सकते हैं :
setTime(prevTime => prevTime + 1);
लेकिन मैं आपको अपना स्वयं का useInterval
हुक बनाने के लिए प्रोत्साहित करूंगा ताकि आप DRY कर सकें और अपने कोड को setInterval
घोषित रूप से उपयोग करके सरल कर सकें , जैसा कि Dan Abramov ने React Hooks के साथ SetInterval Declarative बनाने में यहां बताया है :
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>
सरल और क्लीनर कोड का उत्पादन करने के अलावा, यह आपको स्वचालित रूप से अंतराल को रोकने (और स्पष्ट) की अनुमति delay = null
देता है और अंतराल आईडी भी लौटाता है, यदि आप इसे स्वयं मैन्युअल रूप से रद्द करना चाहते हैं (जो दान के पदों में शामिल नहीं है)।
वास्तव में, इसमें सुधार भी किया जा सकता है, ताकि यह delay
अप्रकाशित होने पर पुनः आरंभ न हो, लेकिन मुझे लगता है कि अधिकांश उपयोग के मामलों के लिए यह काफी अच्छा है।
यदि आप इसके setTimeout
बजाय एक समान उत्तर की तलाश कर रहे हैं setInterval
, तो इसे देखें : https://stackoverflow.com/a/59274757/3723993 ।
तुम भी की कथात्मक संस्करण पा सकते हैं setTimeout
और setInterval
, useTimeout
और useInterval
, के साथ साथ एक कस्टम useThrottledCallback
हुक में टाइपप्रति में लिखा https://gist.github.com/Danziger/336e75b6675223ad805a88c2dfdcfd4a ।