iT邦幫忙

2021 iThome 鐵人賽

DAY 26
0
自我挑戰組

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

[Day26] 在 Codecademy 學 React ~ 你知道用 useState 就能完成簡單小遊戲嗎?大家一起來玩終極密碼吧!

前言

這幾天看到 Codecademy 的 useState 教學,
心裡覺得好適合拿來製作終極密碼遊戲哦XD
其實昨天本來就想弄出來的,
但遇到的問題比昨天弄出的偶像挑選器還多XD
所以就延到今天完成了,
前面還是會先帶 Codecademy 上面的教學,
然後就會進入本日主題─「終極密碼」囉!

本日正文

useState + <input>

昨天介紹了 useState + ...prev 的用法,
今天要講 useState + <input> 的用法。

假設現在有一個畫面,裡面有一個輸入 Email 的 <input>
一開始我們把這個元件放入:<input type="string" />
但如果之後我們想要對輸入的值做一些判斷處理,該怎麼做?

首先我們要在 <input> 裡面加入 value

<input type="string" value={email} />

將 value 指定為 email,
而 email 變數用 useState 進行宣告,
像這樣:

const [email, setEmail] = useState('');

再來我們希望可以取得所輸入的值,
這邊先單純一點先在輸入框下方顯示輸入的內容就好,
所以我在下方增加了一個文字段落,
裡面當然就是要顯示 {email}
像這樣:

return (
    <>
      <h2>請輸入你的Email:</h2>
      <input type="string" value={email} />
      <p>
        請確認你所輸入的Email是否正確:
        <br />
        {email}
      </p>

    </>
  );

https://ithelp.ithome.com.tw/upload/images/20210928/20129873qNbwXIC8VF.png

可是你發現這樣好像不能輸入,
原因是因為 value 的值指向 email 的內容,
但現在 email 的初始值是空字串 useState('')
所以沒有辦法進行任何輸入。

所以現在應該要在 <input> 增加 onChange 的事件,
就是偵測到輸入框有輸入內容的異動,
就要把 email 的值設定為輸入的內容。

這邊宣告一個 函數 handleChange,
裡面要做的事很簡單,
就是將 email 設定為偵測改變的目標物件(input) => event.target 內容值(value),
像這樣:

const handleChange = (event) => {
    setEmail(event.target.value);
  }

然後要記得在 <input> 設定 onChange 要執行 handleChange 函數,
像這樣:

<input type="string" value={email} onChange={handleChange} />

https://ithelp.ithome.com.tw/upload/images/20210928/201298731qn51hxvxs.png

目前這樣看起來初步可以 work 了對吧?
然後就是因為有這個當靈感,
讓我想到這不就很適合拿來做終極密碼的遊戲嗎XD
讀 user 取輸入的數字,
然後判斷現在數字範圍跟是否有猜中數字,
覺得可以XD
那我們現在開始動手吧!

終極密碼 - 前置作業

終極密碼就是要先產生一個數字答案,
然後 user 在範圍內猜一個數字,
會慢慢把範圍縮小,
到最後猜中答案這樣。
(PS. 如果有對終極密碼規則不了解的可以看維基介紹→ 終極密碼 )

思考一下基本元素會有:

  1. 正確答案數字
  2. 輸入的數字
  3. 目前數字範圍

所以我們先新增一個 Game.js,裡面宣告一個 Game 的元件,
我們要先讓三個元素長出來,
前面將這三個元素宣告成狀態,
像這樣:

const [answer, setAnswer] = useState(17);
const [inputNum, setInputNum] = useState();
const [guessRange, setGuessRange] = useState([1,30]);

再來 return 裡面將上面三個元素的值放入,
當然也不要忘記宣告一個 handleInput 函數,
然後因為我們要處理的數字,
但是 input 輸入的內容會 default 被當作 string 處理,
這樣我們會沒辦法進行計算,
所以除了將 inputNum 的值用 setInputNum 設定為 event.target.value 之外,
還要用 parseInt 轉成數字。
另外就是,
我抓很久的問題就是當輸入內容被清空,
此時 parseInt(event.target.value) 就會變成 NaN
然後就會出錯,
這個問題我抓很久= =
所以我還多了一個判斷式,
判斷 event.target.value 不為空才去做轉整數的處理,
為空就直接設成空值,
像這樣:

if (event.target.value !== "") {
      setInputNum(parseInt(event.target.value, 10));
    } else {
      setInputNum("");
    }

函數宣告完後,接著設定 input onChange 要執行 handleInput 函數,
像這樣:

return (
    <>
      <h2>正確答案:{answer}</h2>
      <h3>目前範圍:{guessRange[0]} - {guessRange[1]}</h3>
      <h4>請輸入所猜數字:{inputNum}</h4>
      <input type="number" value={inputNum} onChange={handleInput} />
      <button>送出</button>
    </>
  )

https://ithelp.ithome.com.tw/upload/images/20210928/20129873C2da7qnOB4.png

終極密碼 - 正篇

上面你可以看到有一個送出按鈕,
目的就是希望可以在 user 按下送出按鈕後判斷目前數字情況,
所以再宣告一個 handleChange 函數,裡面要進行終極密碼規則判斷。

const handleChange = (event) => {
      if (
        inputNum !== "" &&
        inputNum > guessRange[0] &&
        inputNum < guessRange[1]
      ) {
        ... (略)
      }
    }
  };

先講解這段,這邊就是要判斷輸入的數字必須要不為空值,且落在目前數字範圍中間,
才繼續以下的邏輯判斷。

...(略)
 if (
        inputNum !== "" &&
        inputNum > guessRange[0] &&
        inputNum < guessRange[1]
      ) {
        if (inputNum > answer) {
          setGuessRange([guessRange[0], inputNum]);
          setInputNum("");
          return;
        }
        if (inputNum < answer) {
          setGuessRange([inputNum, guessRange[1]]);
          setInputNum("");
          return;
        }
        if (inputNum === answer) {
          setStatus("恭喜答對!");
          setInputNum("");
          return;
        }
      }

再來下面這三段,
應該滿好理解的,
就是都是拿輸入數字跟正確答案判斷,

  1. 如果輸入數字比正確答案大,
    表示新的數字範圍將會落在 起始值~輸入數字
  2. 如果輸入數字比正確答案小,
    表示新的數字範圍將會落在 輸入數字~結尾值
  3. 如果輸入數字跟正確答案一樣,
    這就不用多說就是答對了XD
    (PS. 我目前還沒做如果範圍被逼進到中間只夾正確答案要算答對的事XD)

然後當然要記得在送出按鈕的 onClick 指向要執行 handleChange 函數。

<button onClick={handleChange}>送出</button>

終極密碼 - 番外篇

然後為了增加可觀賞性(?),還有遊戲性,
因此增加了 status (遊戲進行狀態),
還有將答案數字隨機產生,(可參考之前大大在鐵人寫的這篇文章→ 用Math.random()取得亂數的技巧) )
像這樣:

const randomNum = Math.floor(Math.random() * 30 + 1);
const [answer, setAnswer] = useState(randomNum);

(PS. 一開始我先把答案寫死,寫完確定邏輯正確後我才把數字改成隨機產生)

然後還加上了重新遊戲的按鈕,
只要 click 就會把目前的數字、狀態等回歸最原始狀態,
像這樣:

const resetState = () => {
    setInputNum("");
    setGuessRange([1, 30]);
    setStatus("進行中");
    setAnswer(Math.floor(Math.random() * 30 + 1));
  };
<button onClick={resetState}>重新遊戲</button>

https://ithelp.ithome.com.tw/upload/images/20210928/201298738ThjwnLF9d.png

終極密碼 - 成果篇

好的,那我們就來試玩看看吧!

是不是很有趣呢XD
當然還有很多地方可以改善啦,
但光用 useState 就可以完成這樣一個小遊戲,
就已經覺得很開心了XD
也算是把 useState 的觀念綜合複習過了一遍~

附上本日程式:
Day26 - useState (Codecademy) - 終極密碼

大家也可以玩玩看哦XD

後記

好像該進入 useEffect 或 Next.js 了?
可是我好像找不到切入點XD
終於來到倒數 4 天!!!!!


上一篇
[Day25] 在 Codecademy 學 React ~ 終於來到 Hook 的世界 ‧ useState 篇 (2)
下一篇
[Day27] 在 Codecademy 學 React ~ 用 useEffect 為遊戲加上計時功能吧!
系列文
Re: 從 Next.js 開始的 React 生活30

尚未有邦友留言

立即登入留言