Như những người khác đã chỉ ra, vấn đề là nó useStatechỉ đượ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 setIntervaltắc, nó sẽ thực sự gọi setTime(time + 1), nhưng timesẽ luôn giữ giá trị mà nó có ban đầu khi lệnh setIntervalgọi lại (đóng) được xác định.
Bạn có thể sử dụng dạng thay thế của useStatesetter 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 useIntervalhook 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 = nullvà 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 delaykhi 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ự setTimeoutthay 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 setTimeoutvà setInterval, useTimeoutvà useInterval, cộng với một phong tục useThrottledCallbackmóc viết bằng bản sao vào https://gist.github.com/Danziger/336e75b6675223ad805a88c2dfdcfd4a .