useReducer
와 컨텍스트 API
로 대체 가능useState
에 winner
와 turn
, tableData
가 필요.tableData
는 3x3으로 만들어주기useStat
는 TicTacToe
파일에서 관리하는데 실제 클릭하는 건 Td
임. (중간에 Table
과 Tr
이 껴있음..)컨텍스트 API
사용하긴 함.useReducer
사용useReducer (reducer, initialState, (지연초기화));
initialState
는 말 그대로 State를 설정해줌. 따로 const로 선언해주기reducer
= 함수state
와 action
의 매개변수를 받음.state
를 어떻게 바꿀지 적어줌.state
에서 initialState
를 만들어 주었기 때문에 state
붙여줘야함onClickTable = useCallback
useCallback
winner
를 'O'로 바꿀 때, dispatch
사용dispatch
안에 들어가는 건 action 객체
dispatch({ type: 'SET_WINNER', winner:'O'});
action 객체 : { type: 'SET_WINNER', winner:'O'}
dispatch
로 실행된다.
const reducer = (state, action) =>{
switch (action.type){
case 'SET_WINNER':
// state.winner = action.winner; 이렇게 하면 안됨!!!
return {
...state, // 새로운 객체를 만들어야 함. 얕은 복사
winner: action.winner,
// 바뀔 부분만 새롭게 바꿔줌. => 불변성
};
}
};
{Array(tableData.length).fill().map((tr,i) => (
<Tr rowIndex = {i} rowData = {tableData[i]} />))}
{Array(rowData.length).fill().map((td, i) => (
<Td rowIndex={rowIndex} cellIndex = {i}>{''}</Td>))}
case CLCIK_CELL: {
const tableData = [...state.tableData]; //기존의 테이블 데이터를
//얕은 복사해줌
tableData[action.row] = [...tableData[action.row]];
//객체가 있으면 얕은 복사 필수
// immer라는 라이브러리로 가독성 해결
tableData[action.row][action.cell] = state.turn;
return {
...state,
tableData,
};
}
새로운 action
을 만들때 전역변수로 만들어줘야하기 때문에 export const
로 정의해줘야함.
turn 바꾸기 - 칸을 누른다음에 턴이 바뀜
case CHANGE_TURN: {
return {
...state,
turn: state.turn === 'O' ? 'X' : 'O',
};
}
dispatch
는 TicTacToe
에서 갖고있기 때문에 td
까지 넘겨줘야함.state
는 비동기이고 비동기인 state
를 처리하려고 한다면 useEffect
를 사용함!useEffect
, useRef
를 넣어 원인 찾기const ref = useRef([]);
useEffect(() => {
consol.log(rowIndex === ref.current[0],rowIndex === ref.current[1],
rowIndex === ref.current[2],rowIndex === ref.current[3]);
//rowIndex, cellIndex, dispatch, cellData 안 바뀔 때가 있어서
//이를 파악하기 위함
ref.current = [rowIndex, cellIndex, dispatch, cellData]; //계속 바뀜.
}, [rowIndex, cellIndex, dispatch, cellData])
react.memo를 이용하는게 쉬움
useMemo 컴포넌트 자체를 기억해버림.