簡體   English   中英

如何在 React 中獲取 setInterval 以更改 state 變量?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM