[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


<<:  [Day 24] 自定义 REST QueryDSL 实现动态查询资料库

>>:  Day15:今天我们来聊一下使用Parrot Security的 Armitage来取得远端系统的存取(Gain Access)权

Day13【Web】网路攻击:DDoS 之 DNS 递回查询攻击

递回查询 Recursive Query 『递回查询』(Recursive Query)是指 当某个...

[DAY 14]cog架构用法(1/2)

目前discord bot已经有初步的功能了 之後写的功能一定会越来越多 决定使用cog的架构来写 ...

【第 29 个第一次】 没创业也该看看的最小可行性产品 MVP 概念,是人生哲学。

Day 29 - 客户说:我是个人户,但是有个理念一直很想要做看看,可以帮我网站报价吗,概念是这样....

<Day12>浅谈什麽是股票、期货、选择权、指数?

● 这篇主要是写给初出茅芦且目前还是身为投资小白的自己 在分项介绍之前,先搞懂什麽是"金融...

Day13 原来Background可以有这麽多设定

Background包含哪些属性? Background是缩写,包含了以下几个CSS属性 back...