Reactを学習することになったのでチュートリアルをやってみようと思う。しかし、チュートリアルはTypescriptではないので、調べながら変更していこうと思う。
前回からのつづき
ゲームを完成させる
手番の処理
- 先手を×にするためにstateの初期値を設定する。
const Board = () => {
const [xIsNext, setXIsNext] = React.useState<boolean>(true);
- マス目をクリックするたびに、xIsNexsの値を反転させ、先手後手を入れ替える。
const handleClick = (i: number) => {
const sq = squares.slice();
sq[i] = xIsNext ? 'X' : 'O';
setSquares(sq);
setXIsNext(prev => !prev);
};
setXIsNext(prev => !prev);
stateを変更する関数は、現在の値を受け取ることができるため、現在の値を使用して新しい値を設定する場合、上記のような書き方ができる。
- どちらのプレーヤーの手番なのかを表示する。
const status = 'Next player: ' + (xIsNext ? 'X' : 'O');
クリックするたび、”×”と”○”が切り替わるようになった。
ゲーム勝者の判定
- 判定用関数を記述する。9つのsquareの配列を渡すと勝者を判定し、”×”、”○”あるいはnullを返却する。
const calculateWinner = (squares: StateType[]) => {
const lines = [
[0, 1, 2], // 横
[3, 4, 5],
[6, 7, 8],
[0, 3, 6], // 縦
[1, 4, 7],
[2, 5, 8],
[0, 4, 8], // 斜め
[2, 4, 6],
];
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a];
}
}
return null;
}
- 勝者を判定して表示する。
// ↓ 追加
const winner = calculateWinner(squares);
let status;
if (winner) {
status = 'Winner: ' + winner;
} else {
status = 'Next player: ' + (xIsNext ? 'X' : 'O');
}
// ↑ 追加
// const status = 'Next player: ' + (xIsNext ? 'X' : 'O'); // ←削除
Board.tsx
のhandleClick
を書き換えて、勝敗が決まっている場合やクリックされたマス目が埋まっている場合は、早期にreturnする。
const handleClick = (i: number) => {
const sq = squares.slice();
// ↓ 追加
if (calculateWinner(sq) || sq[i]) {
return;
}
// ↑ 追加
sq[i] = xIsNext ? 'X' : 'O';
setSquares(sq);
setXIsNext(prev => !prev);
};
勝者が表示されるようになった。
import Board from './Board';
const App = () => {
return (
<div className='game'>
<div className='game-board'>
<Board />
</div>
<div className='game-info'>
<div>{/* status */}</div>
<ol>{/* TODO */}</ol>
</div>
</div>
);
};
export default App;
import React from "react"; // 追加
import Square, {StateType} from "./Square";
const calculateWinner = (squares: StateType[]) => {
const lines = [
[0, 1, 2], // 横
[3, 4, 5],
[6, 7, 8],
[0, 3, 6], // 縦
[1, 4, 7],
[2, 5, 8],
[0, 4, 8], // 斜め
[2, 4, 6],
];
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a];
}
}
return null;
}
const Board = () => {
const [xIsNext, setXIsNext] = React.useState<boolean>(true);
const [squares, setSquares] = React.useState<StateType[]>(
[null, null, null,
null, null, null,
null, null, null,]
);
const handleClick = (i: number) => {
const sq = squares.slice();
if (calculateWinner(sq) || sq[i]) {
return;
}
sq[i] = xIsNext ? 'X' : 'O';
setSquares(sq);
setXIsNext(prev => !prev);
};
const renderSquare = (i: number) => {
return <Square
value={squares[i]}
onClick={() => handleClick(i)} // 追加
/>;
};
const winner = calculateWinner(squares);
let status;
if (winner) {
status = 'Winner: ' + winner;
} else {
status = 'Next player: ' + (xIsNext ? 'X' : 'O');
}
// const status = 'Next player: ' + (xIsNext ? 'X' : 'O');
return (
<div>
<div className="status">{status}</div>
<div className="borad-row">
{renderSquare(0)}
{renderSquare(1)}
{renderSquare(2)}
</div>
<div className="borad-row">
{renderSquare(3)}
{renderSquare(4)}
{renderSquare(5)}
</div>
<div className="borad-row">
{renderSquare(6)}
{renderSquare(7)}
{renderSquare(8)}
</div>
</div>
);
};
export default Board;
type SquareProps = {
value: StateType;
onClick: VoidFunction;
};
export type StateType = 'O' | 'X' | null;
const Square = (props: SquareProps) => {
return (
<button
className="square"
onClick={props.onClick}
>
{props.value}
</button>
);
};
export default Square;
コメント