728x90
1. React Hooks
Hooks는 리액트 v16.8에 새로 도입된 기능으로, 함수형 컴포넌트에서 상태와 생명주기 기능을 사용할 수 있도록 해주는 기능이다. 함수 컴포넌트에서도 상태 관리를 할 수 있는 useState, 렌더링 직후 작업을 설정하는 udeEffect 등의 기능을 제공한다.
지금 앱은 클래스형 컴포넌트로 작성했지만 Hooks를 사용하기 위해 함수형 컴포넌트로 바꾼다. 함수형에서는 render()없이 바로 리턴한다.
Board.js
import React, { Component } from 'react';
import Square from './Square';
import "./Board.css";
const Board = () => {
constructor(props) {
super(props);
this.state = {
squares: Array(9).fill(null)
}
}
handleClick(i) {
const squares = this.state.squares.slice();
squares[i] = 'X';
this.setState({ squares: squares })
}
renderSquare(i) {
return <Square value={this.state.squares[i]} onClick={() => this.handleClick(i)} />
}
return (
<div>
<div className="status">Next Player: X, O</div>
<div className="board-row">
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
</div>
<div className="board-row">
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
</div>
<div className="board-row">
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
</div>
</div>
)
};
export default Board;
state부분은 Hooks의 useState로 바꾼다.
const Board = () => {
const [squares, setSquares] = useState(Array(9).fill(null));
첫 번째 원소는 상태 값(getter), 두 번째 원소는 상태를 설정하는 함수(setter)이다.
나머지 메소드도 화살표 함수로 바꾸고 this.state 부분은 삭제한다.
const handleClick = (i) => {
const newSquares = squares.slice();
newSquares[i] = 'X';
setSquares(newSquares)
}
const renderSquare = (i) => {
return <Square value={squares[i]} onClick={() => handleClick(i)} />
}
return (
<div>
<div className="status">Next Player: X, O</div>
<div className="board-row">
{renderSquare(0)}
{renderSquare(1)}
{renderSquare(2)}
</div>
<div className="board-row">
{renderSquare(3)}
{renderSquare(4)}
{renderSquare(5)}
</div>
<div className="board-row">
{renderSquare(6)}
{renderSquare(7)}
{renderSquare(8)}
</div>
</div>
)
Square.js
구조 분해 할당으로 코드를 간결하게 표현한다.
const Square = ({ onClick, value }) => {
return (
<button className='square' onClick={onClick}>
{value}
</button>
);
};
export default Square;
728x90
2. 순서 만들기
X와 O가 번갈아가면서 입력되도록 한다.
xIsNext 변수를 생성하고 xIsNext가 true일 때는 X를, false일 때는 O를 입력하도록 한다.
const Board = () => {
const [squares, setSquares] = useState(Array(9).fill(null));
const [xIsNext, setXIsNext] = useState(true);
const handleClick = (i) => {
const newSquares = squares.slice();
newSquares[i] = xIsNext ? 'X' : 'O';
setSquares(newSquares)
setXIsNext(!xIsNext);
}
status 태그 부분에도 다음 플레이어가 누구 차례인지 보이도록 한다.
const [squares, setSquares] = useState(Array(9).fill(null));
const [xIsNext, setXIsNext] = useState(true);
const status = `Next player: ${xIsNext ? 'X' : 'O'}`;
return (
<div>
<div className="status">{status}</div>
3. 승자 결정
승자 판별을 위해 승부 결정 계산 함수를 만든다. X나 O가 한 줄이 되는 경우의 수를 나열하고 한 줄이 나오면 해당 플레이어를 return한다.
const Board = () => {
const [squares, setSquares] = useState(Array(9).fill(null));
const [xIsNext, setXIsNext] = useState(true);
const calculateWinner = (squares) => {
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);
승자가 판별되면 status가 winner로 바뀌게 한다.
const winner = calculateWinner(squares);
let status;
if (winner) {
status = 'Winner: ' + winner;
} else {
status = `Next player: ${xIsNext ? 'X' : 'O'}`;
}
승자가 판별되거나 board에 값이 있으면 handleClick 메서드에서 클릭 이벤트를 무시하도록 한다.
const handleClick = (i) => {
const newSquares = squares.slice();
if(calculateWinner(newSquares) || newSquares[i]){
return;
}
728x90
'React' 카테고리의 다른 글
[React / Gsap] 스크롤과 터치에 따른 애니메이션 전환 효과 구현 (1) | 2024.09.27 |
---|---|
[React] TicTacToe앱 만들기(3) - 게임 동작 기록, jumpTo() (0) | 2024.06.20 |
[React] TicTacToe앱 만들기(1) - 컴포넌트 생성, props, state (1) | 2024.06.14 |
[React] 번들러 | 리액트 컴포넌트 | JSX (0) | 2024.06.12 |
[React] 리액트 프로젝트 생성(create-react-app) (1) | 2024.06.12 |