[英]How to get setInterval in React to change a state variable?
我希望以下代碼在用戶登錄后從一個數字開始倒計時。
以下代碼顯示了簡單的每秒在 console.log 中顯示0
,但似乎沒有將 state 變量secondsLeft
設置為 8,也沒有將此變量計數。
const [secondsLeft, setSecondsLeft] = useState(0);
...
const handleButton = async () => {
const response = await fetch('http://localhost:5001/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
setUsername('');
setPassword('');
if (response.ok) {
const data = await response.json();
setCurrentUser(prev => ({ ...prev, ...data.user }));
setMessage(`User: ${currentUser.firstName}`);
setSecondsLeft(8);
setInterval(() => {
setSecondsLeft(prev => secondsLeft -1);
console.log(secondsLeft);
}, 1000);
} else {
setMessage('bad login');
}
}
如何讓 setInterval 每秒減少secondsLeft
的值?
這是 React 中一個非常常見的陷阱。 可以理解,您似乎假設setSecondsLeft
立即生效,但事實並非如此。 因此, secondsLeft
開始就不是 8。 還有一個問題是secondsLeft
在您的時間間隔內沒有更新。 這應該起作用:
setSecondsLeft(8);
setInterval(() => {
setSecondsLeft(prev => prev - 1);
console.log(prev);
}, 1000);
我試着用傳統的方式做事,
useEffect(() => {
setInterval(() => {
console.log("update", st);
setSt((prev) => prev + 1);
}, 500);
}, []);
但這不起作用,因為在第一次渲染時運行useState
期間,setInterval 為自己保留了copy of prev
它會嘗試一次又一次地更新,例如: setSt(prev => prev+1)
但因為 prev總是 = 1,因為它自己的閉包,值並沒有真正更新。
然后我嘗試使用useCallback
鈎子,它在依賴項更改時function
並因此提供prev
作為依賴項,但遺憾的是,由於與上述相同的原因但通過不同的方式,這很可能也不起作用。
您可以在代碼和codesandbox
的注釋掉代碼中看到我嘗試的方式。
最后,我嘗試了創建一個setIntervalHack
,它的工作原理類似於 setInterval,但不是真正的 setInterval。
這是我遵循的步驟。
setInterval as a recursive
function ,它會在一個間隔后一次又一次地運行,但缺點是我們無法將參數傳遞給它,因為更新變得陳舊。setIntervalHack
傳遞了一個參數,該參數描述了要更新的新 state。await
遞歸再次運行,並在調用遞歸 function setIntervalHack
的參數中,我通過了(currentState+1) 這就是我試圖實現setInterval
類似功能的方式
這是代碼https://codesandbox.io/s/happy-swartz-ikqdn?file=/src/focus.js的鏈接
注意:您可以 go 在codesandbox's browser
中的 /focus 路徑上並打開控制台
PS:我對計數器進行了增量,而不是減量。 您可以通過減少每次遞歸來做類似的事情
實際上它可以工作,但是每次 setIntervall 回調運行時您都會在控制台8中看到,因為它只訪問 secondsLeft 的初始值,並且在以后的更新中它不識別 secondsLeft 的更新。 您可以運行以下示例並查看它更新 state 每次 setInterval 回調運行,但在控制台中您總是看到 0。
function App() { const [secondsLeft, setSeconds] = React.useState(0); let timerId = null; const handleClick = () => { setSeconds(8); timerId = setInterval(()=> { console.log(secondsLeft) setSeconds(prev => prev - 1); }, 1000) }; React.useEffect(()=> { return ()=> clearInterval(timerId) }, []) return ( <div> <h2>{secondsLeft}</h2> <button onClick={handleClick}>Start interval</button> </div> ); } ReactDOM.render(React.createElement(App, {}), document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script> <div id="root"></div>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.