다른 사람들이 지적했듯이 문제는 useState
한 번만 호출 된다는 것 입니다 ( deps = []
간격 설정 :
React.useEffect(() => {
const timer = window.setInterval(() => {
setTime(time + 1);
}, 1000);
return () => window.clearInterval(timer);
}, []);
그런 다음 setInterval
틱이 발생할 때마다 실제로를 호출 setTime(time + 1)
하지만 콜백 (클로저)이 정의 time
되었을 때 처음에 가지고 있던 값을 항상 보유합니다 setInterval
.
useState
의 setter 의 대체 형식을 사용하고 설정하려는 실제 값 대신 콜백을 제공 할 수 있습니다 (와 마찬가지로 setState
).
setTime(prevTime => prevTime + 1);
그러나 Dan Abramov가 Make setInterval Declarative with React Hooks 에서 제안한 것처럼 선언적useInterval
으로 사용하여 코드를 건조하고 단순화 할 수 있도록 자체 후크 를 생성하는 것이 좋습니다 .setInterval
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
, 수동으로 취소하려는 경우 (Dan의 게시물에서 다루지 않음) 전달하여 간격을 자동으로 일시 중지 (및 삭제) 하고 간격 ID를 반환 할 수 있습니다.
실제로 이것은 delay
일시 중지 되지 않은 상태에서 다시 시작되지 않도록 개선 할 수도 있지만 대부분의 사용 사례에서이 정도면 충분하다고 생각합니다.
setTimeout
대신 에 대한 비슷한 답변을 찾고 있다면 https://stackoverflow.com/a/59274757/3723993을setInterval
확인 하십시오 .
선언적 버전 setTimeout
및 setInterval
, useTimeout
및 및 TypeScript로 작성된 useInterval
사용자 지정 useThrottledCallback
후크를 https://gist.github.com/Danziger/336e75b6675223ad805a88c2dfdcfd4a 에서 찾을 수도 있습니다 .