- useReducer
- 상태를 하나의 객체로 모아서 관리해주는 React의 Hooks
- action, reducer, dispatch의 패턴으로 동작
- action : 상태를 변화시키는 행동, 의사표현(객체)
- reducer : action에 따른 상태를 변화시키는 로직, 상태의 불변성 유지해야함
- dispatch : action을 실행시키는 함수
- 사용 방법
- 초기상태 및 reducer 함수 정의
- useReducer로 state, dispatch 가져오기
- 액션객체의 type은 상수로 관리하는 것이 좋다.
import React, { useEffect, useReducer, useCallback } from 'react';
import Table from './Table';
const initialState = {
winner: '',
turn: 'O',
tableData: [
['', '', ''],
['', '', ''],
['', '', ''],
],
recentCell: [-1, -1],
};
export const SET_WINNER = 'SET_WINNER';
export const CLICK_CELL = 'CLICK_CELL';
export const CHANGE_TURN = 'CHANGE_TURN';
export const RESET_GAME = 'RESET_GAME';
const reducer = (state, action) => {
switch (action.type) {
case SET_WINNER:
return {
...state,
winner: action.winner,
};
case CLICK_CELL: {
const tableData = [...state.tableData];
tableData[action.row] = [...tableData[action.row]];
tableData[action.row][action.cell] = state.turn;
return {
...state,
tableData,
recentCell: [action.row, action.cell],
};
}
case CHANGE_TURN: {
return {
...state,
turn: state.turn === 'O' ? 'X' : 'O',
};
}
case RESET_GAME: {
return {
...state,
turn: 'O',
tableData: [
['', '', ''],
['', '', ''],
['', '', ''],
],
recentCell: [-1, -1],
};
}
default:
return state;
}
};
const TicTacToe = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const { tableData, turn, winner, recentCell } = state;
const onClickTable = useCallback(() => {
dispatch({ type: SET_WINNER, winner: 'O' });
}, []);
useEffect(() => {
const [row, cell] = recentCell;
if (row < 0) {
return;
}
let win = false;
if (tableData[row][0] === turn && tableData[row][1] === turn && tableData[row][2] === turn) {
win = true;
}
if (tableData[0][cell] === turn && tableData[1][cell] === turn && tableData[2][cell] === turn) {
win = true;
}
if (tableData[0][0] === turn && tableData[1][1] === turn && tableData[2][2] === turn) {
win = true;
}
if (tableData[0][2] === turn && tableData[1][1] === turn && tableData[2][0] === turn) {
win = true;
}
console.log(win, row, cell, tableData, turn);
if (win) {
dispatch({ type: SET_WINNER, winner: turn });
dispatch({ type: RESET_GAME });
} else {
let all = true;
tableData.forEach((row) => {
row.forEach((cell) => {
if (!cell) {
all = false;
}
});
});
if (all) {
dispatch({ type: SET_WINNER, winner: null });
dispatch({ type: RESET_GAME });
} else {
dispatch({ type: CHANGE_TURN });
}
}
}, [recentCell]);
return (
<>
<Table onClick={onClickTable} tableData={tableData} dispatch={dispatch} />
{winner && <div>{winner}님의 승리</div>}
</>
)
};
export default TicTacToe;