iT邦幫忙

2021 iThome 鐵人賽

DAY 27
0
自我挑戰組

Re: 從 Next.js 開始的 React 生活系列 第 27

[Day27] 在 Codecademy 學 React ~ 用 useEffect 為遊戲加上計時功能吧!

前言

想了想還是決定把 useEffect 走完XD
不然有講 useState 沒講 useEffect 好像哪裡怪怪的XD???
所以就拿昨天寫的終極密碼遊戲,加上計時功能吧!

本日正文

Hook - useEffect

還記得之前介紹過的 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>}

https://ithelp.ithome.com.tw/upload/images/20210929/20129873edcrJLWaUI.png

開始計時

我們當然要先新增一個狀態存計時:

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


上一篇
[Day26] 在 Codecademy 學 React ~ 你知道用 useState 就能完成簡單小遊戲嗎?大家一起來玩終極密碼吧!
下一篇
[Day28] 又回到最初的起點 ~ Next.js
系列文
Re: 從 Next.js 開始的 React 生活30

尚未有邦友留言

立即登入留言