想了想還是決定把 useEffect 走完XD
不然有講 useState 沒講 useEffect 好像哪裡怪怪的XD???
所以就拿昨天寫的終極密碼遊戲,加上計時功能吧!
還記得之前介紹過的 useEffect 嗎?
現在學過生命週期再看官方文件,
終於看得懂它在說什麼了QQ
資料 fetch、設定 subscription、或手動改變 React component 中的 DOM 都是 side effect 的範例。無論你是否習慣將這些操作稱為「side effect」(或簡稱「effect」),你之前可能已經在 component 中執行了這些操作。
提示:
如果你熟悉 React class 的生命週期方法,你可以把 useEffect 視為 componentDidMount,componentDidUpdate 和 componentWillUnmount 的組合。
因為計時是屬於 component render 後要更新的情況,
我們就可以使用 useEffect 這個 Hook。
(之前的寫法我們可能會將開始計時跟停止分別寫在 componentDidMount, componentWillUnmount 上)
再來我們要在遊戲開始進行計時,每秒更新一次,
因此又要派出我們熟悉的 setInterval
啦!
想要讓 user 按下開始遊戲才正式計時,
因此在畫面上要新增「開始遊戲」的按鈕,
而昨天遊戲只分成進行中跟恭喜答對兩種狀態,
所以勢必要新增一個狀態「尚未開始」,
狀態是「尚未開始」就出現「開始遊戲」的按鈕,
點選「開始遊戲」的按鈕狀態就變為「進行中」,接著出現遊戲畫面,並開始計時,
而當然答對後就要停止計時。
{status === "尚未開始" && <button onClick={startGame}>開始遊戲</button>}
{status === "進行中" ? (
<>
<hr />
<h3>
目前範圍:{guessRange[0]} - {guessRange[1]}
</h3>
<h4>請輸入所猜數字:{inputNum}</h4>
<input type="number" value={inputNum} onChange={handleInput} />
<button onClick={handleChange}>送出</button>
</>
): null }
{status === "恭喜答對!" && <button onClick={resetState}>重新遊戲</button>}
我們當然要先新增一個狀態存計時:
const [count, setCount] = useState(0);
再來就很開心用 useEffect 裡面寫 setInterval 開始計時啦,
計時的方式是利用 setInterval 每一秒執行一次 setCount,
每秒將 count +1
,
像這樣:
useEffect(() => {
setInterval(() => setCount((c) => c + 1), 1000);
}, []);
然後也在畫面上加上 {count}
來看成果吧!
等等,為什麼還沒開始遊戲就開始計時了?
因為用 useEffect 會在 render 後就開始執行,
這邊沒有設定任何條件,
當然是元件都 render 完後就開始計時,
因此這邊我們要多加一個 flag start
來控制計時與否。
像這樣:
... (略)
const [start, setStart] = useState(false);
... (略)
useEffect(() => {
if (start) {
setInterval(() => setCount((c) => c + 1), 1000);
}
}, [start]);
(PS. [start]
意思是當 start 有變化時才去執行這段)
然後不要忘記在開始遊戲的按鈕 onClick 綁定 startGame 函數,
startGame 裡面要做的事就是將 start 改為 true,還有 status 改為進行中,
像這樣:
const startGame = () => {
setStart(true);
setStatus("進行中");
};
好,再讓我們看一次成果吧!
等等,雖然發現計時功能有 work,
但答對之後忘了停止計時,
還記得我們之前在介紹生命週期時有講過停止計時,
因此我們要將 clearInterval 寫進來,
像這樣:
const id = setInterval(() => setCount((c) => c + 1), 1000);
return () => clearInterval(id);
也別忘記在答對的時候將 start
設為 false,
讓它不會再走到繼續計時的條件,
像這樣:
if (inputNum === answer) {
setStatus("恭喜答對!");
setInputNum("");
setStart(false);
return;
}
好,再讓我們看一次成果吧!
很好!這次終於對了!
可喜可賀!
也別忘記在重新遊戲綁定的函數 resetState 將計數歸0,以及將 start 的值設為 false,
像這樣:
setStart(false);
setCount(0);
附上今日程式:Day27 - useState (Codecademy) - 終極密碼 (計時版)
其實今天在寫計時上面遇到沒有開始計時、沒有停止計時等問題,
我是看大大寫的這篇才解決問題的,
這邊也提供大家參考→ 「前端攻城詩」從計數器開始的React Hook 人生
感恩大大!!!
來到倒數 3 天!說好的 Next.js 該登場了吧XD