React- useReducer

Jenna·2023년 1월 6일
1

React

목록 보기
3/3
post-thumbnail

틱택토 예제로 알아보는 useReducer


useState로 상태관리를 해주었지만 useState말고도 useReducer를 이용한 상태관리가 가능하다.
useReducer를 사용하면 컴포넌트의 상태 업데이트를 다른 파일에 불러와서 사용할 수도 있고, 컴포넌트 바깥에서 사용할 수도 있다.


💫 코드보기


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;
        }
        if (win) { //승리시
            dispatch({ type: SET_WINNER, winner: turn });
            dispatch({ type: RESET_GAME })
        } else { //무승부 
            let all = true; // -> all이 true면 무승부라는 뜻
            tableData.forEach((row) => {
                row.forEach((cell) => {
                    if (!cell) {
                        all = false;
                    }
                })
            })
            if (all) {

            } else {
                dispatch({ type: CHANGE_TURN });
            }
        }
    }, [recentCell]);
    return (
        <>
            <Table onClick={onClickTable} tableData={tableData} dispatch={dispatch} />
            {winner && <div>{winner}님의 승리</div>}
        </>
    )
}

코드 작성시 주의할 점

const reducer = (state, action) => {
    switch (action.type) {
        case SET_WINNER:
            //state.winner = action.winner 이렇게 하면 안됨. 복사해서 바꿔줘야됨.
            return {
                ...state,
                winner: action.winner,
            };

위 예제에서 state.winner = action.winner로 써주게 되면 리액트의 불변성을 해치게 되서 오류가 날 수 있다.
...state, winner : action.winner 로 작성해줘야 된다.


const initialState = {
    winner: '',
    turn: 'O',
    tableData: [
        ['', '', ''],
        ['', '', ''],
        ['', '', '']
    ],
    recentCell: [-1, -1],
};

Type값은 대문자와 _ 로 이루어지지만 관습일 뿐 꼭 그렇게 지켜져야하는 것은 아니다.

profile
connecting the dots 💫

0개의 댓글