Skip to main content

[TS] React-hangman with TS & Vite

這是一個用 Vite 建立專案,與使用 TS 和 React 框架製作的小遊戲,叫做 hangman 的重點筆記。

GitHub Repo

Vite

Vite 幫助我們快速建立專案的模板,跟npx create-react-app很相似,但是有個小差別在於,使用 Vite 建立好的模板,要再特別下指令下載 npm packages,才能執行。程式碼參考如下

npm create vite@latest

輸入Project name,選擇react,再選擇typescript

建立完後,輸入以下指令,便可以啟動專案

cd vite-project
npm install
npm run dev

useEffect & useCallback

useEffect控制整個程式的 lifecycle,可以讓程式因第二個參數的變化,而做更新。

useCallback回傳一個被記錄的函式,透過第二個參數來去做更新。

src/App.tsx
// 透過 useCallback 我們可以紀錄這個函式的狀態,只會在第二個參數有變化時,重新定義此函式
const addGuessedLetter = useCallback(
(letter: string) => {
console.log(guessedLetters);
if (guessedLetters.includes(letter) || isLoser || isWinner) return;

setGuesedLetters((currentLetters) => [...currentLetters, letter]);
console.log("set!");
},
[guessedLetters, isWinner, isLoser]
);

useEffect(() => {
const handler = (e: KeyboardEvent) => {
const key = e.key;

if (!key.match(/^[a-z]$/)) return;

e.preventDefault();
console.log("run");
addGuessedLetter(key);
};

document.addEventListener("keypress", handler);

return () => {
document.removeEventListener("keypress", handler);
};
}, [guessedLetters]); // 第二參數 guessedLetters 的設置使得 App.tsx 在 guessedLetters 改變時會更新,就不會出現輸入重複字母連續答錯畫線的情況

Styles

一種是靜態的 CSS,直接寫在該元素的style

src/Keyboard.tsx
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(75px, 1fr)",
gap: ".5rem",
}}
>
// ...
</div>

一種是匯入外部寫好的 CSS,透過 JS 控制className屬性來達到變化。

外部 CSS 如下

src/Keyboard.module.css
.btn.active {
background-color: hsl(200, 100%, 50%);
color: white;
}

.btn.inactive {
opacity: 0.3;
}

匯入時,定義為styles,之後像是物件一樣使用styles

src/Keyboard.tsx
import styles from "./Keyboard.module.css";
// ...
{
KEYS.map((key) => {
const isActive = activeLetters.includes(key);
const isInactive = inactiveLetters.includes(key);
return (
<button
onClick={() => addGuessedLetter(key)}
className={`${styles.btn} ${isActive ? styles.active : ""} ${
isInactive ? styles.inactive : ""
}`}
disabled={isInactive || isActive || disabled}
key={key}
>
{key}
</button>
);
});
}

透過上方程式碼,我們可以也可看到,當有多個className要定義時,我們可以透過${} (Template literals)來去定義。

TS props 傳遞

在上層元素傳遞資料給下層元素時,會透過props,像是

src/App.tsx
<HangmanWord
reveal={isLoser}
guessedLetters={guessedLetters}
wordToGuess={wordToGuess}
/>

在下層要定義好props的類別

src/HangmanWord.tsx
type HangmanWordProps = {
reveal?: boolean,
guessedLetters: string[],
wordToGuess: string,
};

export default function HangmanWord({
guessedLetters,
wordToGuess,
reveal = false,
}: HangmanWordProps) {
// ...
}

TS 會幫我們抓出像是名稱不相同、類別不同的錯誤,十分的精準;不像平常寫 React,要到 localhost 開啟後,報錯才知道。

結論

Vite 十分地快速,透過此次的練習,我也更了解到 TS 的精準性,以及 React 因狀態不同而有對應的 UI(CSS)。未來,這個 hangman 遊戲還有許多進步空間,我想說目前難度太高,可以透過選擇類別、先提供會出現的單字清單,記憶後再進行遊戲來降低難度,上述也是我參考其他 hangman 遊戲的想法。

參考資料