【React】使用React Hooks开发一个打字小游戏
前言
本文主要内容:通过开发一个打字小游戏页面的各个功能,了解各种react hooks的使用
在之前的文章中已经介绍过useState
和useEffect
这两个react hooks了,也确实在具体的开发过程中体会到了react hooks带来的便利,在下面将会介绍更多的react hooks
项目预览与分析
可以看到程序的基本架构代码如下
import React from "react"
export default function App() {
return (
<div>
<h1>How fast do you type?</h1>
<textarea />
<h4>Time remaining: ???</h4>
<button>Start</button>
<h1>Word count: ???</h1>
</div>
)
}
我们需要实现的功能如下:
- 指定打字时间倒数
- 对输入文字计数
- 点击按钮重新开始
- ...
useState和useEffect
获取输入的字符
要对输入的字段计数,首先要获得我们输入的字符。代码如下
const [text,setText] = useState("")
function handleChange(e) {
const {value} = e.target
setText(value)
}
<textarea
onChange={handleChange}
value={text}
/>
这里textarea中的value={text}
是一个先有鸡还是先有蛋的问题,总之就是我们要获取textarea的值,将其设置为state,并且重新将值设置为我们输入的。
对输入的字符计数
要实现的功能是点击start按钮就会返回text中的单词的个数,由于是个算法问题,不在这占过多的篇幅,计数的函数代码如下
function calculateWordCount(myText) {
const wordsArr = myText.trim().split(" ")
return wordsArr.filter(word => word !== "").length
}
可以看到用了三步,split是将一整个string根据空格进行分割然后生成数组,trim是去除字符串首尾的空格,filter过滤了空字符串,共同确保了能返回正确的单词数
以及对函数的调用如下
<button onClick={() => calculateWordCount(text)}>Start</button>
设置时间倒数功能
在将获得的单词计数输出到屏幕之前,先设置一个时间倒数的功能
同样是设置一个state,然后写一个倒数的函数,如下
const [timeRemaining, setTimeRemaining] = useState(5)
useEffect(() =>{
if(timeRemaining > 0){
setTimeout(() => {
setTimeRemaining(time => time - 1)
}, 1000)
}
}, [timeRemaining])
如果时间大于0,那么每隔1000ms都会将time state - 1。这样就完成了时间的倒数
不过我们希望是在点击start按钮后才进行倒数,所以可以通过再添加一层验证来实现,如下
const [isTimeRunning, setIsTimeRunning] = useState(false)
useEffect(() =>{
if(timeRemaining > 0 && isTimeRunning){
setTimeout(() => {
setTimeRemaining(time => time - 1)
}, 1000)
}else if(timeRemaining === 0) {
setIsTimeRunning(false)
}
}, [timeRemaining, isTimeRunning])
<button onClick={() => setIsTimeRunning(true)}>Start</button>
设置了一个新的state,初始值为false,然后按钮函数为设置这个state为true。注意这里需要在useEffect的dependencis中加入isTimeRunning,从而让其能够运行里面的代码。
在时间为0时输出单词数
同样是设置一个state,然后在时间为0时调用之前写好的calculateWordCount函数,将函数的返回值设置为这个state的值,代码如下
const [wordCount, setWordCount] = useState(0)
}else if(timeRemaining === 0) {
setIsTimeRunning(false)
setWordCount(calculateWordCount(text))
}
结束后点击按钮重新开始
每次游戏结束后都需要刷新页面来重新开始,所以现在的需求是在按钮上设置一个可以重新开始的功能。为了重新开始,我们需要设置时间开关、剩余时间以及文本框内容,所以按钮函数如下
function startGame() {
setIsTimeRunning(true)
setTimeRemaining(5)
setText("")
}
这样我们就能实现结束游戏后,点击按钮再一次开始
然而当我们多次点击会发现出现一些问题,所以设置一个点击一次后按钮失效的功能,代码如下
<button
onClick={startGame}
disabled={isTimeRunning}
>
Start
</button>
当isTimeRunning,disabled为true,按钮失效。
同样也可以对textarea进行类似设置,并且设置一下textarea背景的颜色用作提示,如下
textarea:disabled {
background-color: #a5a2a2;
}
所以目前的实现效果如下
useRef
实现自动切换光标
接下来我们想要对功能进一步地优化,实现点击start按钮后,可以直接输入字符,而不用再点击文本输入框。
这需要用到 react hooks 中 useRef 的功能,首先定义一个useRef如下
const textBoxRef = useRef(null)
,
然后为需要focus的dom元素添加ref,此处给textarea添加ref={textBoxRef}
最后使用这个ref,在start按钮的函数编写语句textBoxRef.current.focus()
然而此时并不能起效果,因为textarea被disable了,所以要先textBoxRef.current.disabled = false
这样就能在点击start按钮后,自动将光标移到文本输入框中
custom hooks
生成hooks单独文件
最后对代码进行优化,为了避免App组件中的内容过多,我们创建一个useWordGame.js作为提供hooks的单独文件,需要使用时引入即可,所以将hooks相关的代码全部移到新文件中,然后使用如下代码输出
return {textBoxRef, handleChange, text, isTimeRunning, timeRemaining, startGame, wordCount}
之后就是在App中接收并使用这些输出的hooks,如下
import useWordGame from "./useWordGame"
const {
textBoxRef,
handleChange,
text,
isTimeRunning,
timeRemaining,
startGame,
wordCount
} = useWordGame()
所以我们就完成了这个打字小游戏的开发,效果如下
小结
粗略地使用了一些react hooks相关的内容开发了一个网页打字游戏,其中值得关注的有useRef和custom hooks两个新知识点,前者用于定位dom元素,后者是将react hooks单独生成文件的方式。
项目原地址:scrimba.com/learn/react…
我的GitHub地址:github.com/newfish-cmy…
转载自:https://juejin.cn/post/7163167702578626596