Như những người khác đã chỉ ra, vấn đề là nó useState
chỉ được gọi một lần (vì nó deps = []
để thiết lập khoảng thời gian:
React.useEffect(() => {
const timer = window.setInterval(() => {
setTime(time + 1);
}, 1000);
return () => window.clearInterval(timer);
}, []);
Sau đó, mỗi khi tích setInterval
tắc, nó sẽ thực sự gọi setTime(time + 1)
, nhưng time
sẽ luôn giữ giá trị mà nó có ban đầu khi lệnh setInterval
gọi lại (đóng) được xác định.
Bạn có thể sử dụng dạng thay thế của useState
setter và cung cấp một lệnh gọi lại thay vì giá trị thực mà bạn muốn đặt (giống như với setState
):
setTime(prevTime => prevTime + 1);
Nhưng tôi khuyến khích bạn tạo useInterval
hook của riêng mình để bạn có thể KHÔ và đơn giản hóa mã của mình bằng cách sử dụng một cách setInterval
khai báo , như Dan Abramov đề xuất ở đây trong Tạo setInterval Decl Compare với React Hooks :
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>
Ngoài việc tạo ra mã đơn giản hơn và rõ ràng hơn, điều này cho phép bạn tạm dừng (và xóa) khoảng thời gian tự động bằng cách vượt qua delay = null
và cũng trả về ID khoảng thời gian, trong trường hợp bạn muốn tự hủy nó theo cách thủ công (điều này không được đề cập trong các bài viết của Dan).
Trên thực tế, điều này cũng có thể được cải thiện để nó không khởi động lại delay
khi không bị tạm dừng, nhưng tôi đoán đối với hầu hết các trường hợp sử dụng, điều này là đủ tốt.
Nếu bạn đang tìm kiếm câu trả lời tương tự setTimeout
thay vì tìm kiếm setInterval
, hãy xem phần này: https://stackoverflow.com/a/59274757/3723993 .
Bạn cũng có thể tìm thấy phiên bản tường thuật của setTimeout
và setInterval
, useTimeout
và useInterval
, cộng với một phong tục useThrottledCallback
móc viết bằng bản sao vào https://gist.github.com/Danziger/336e75b6675223ad805a88c2dfdcfd4a .