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 = useCallbackuseCallbackwinner를 '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 컴포넌트 자체를 기억해버림.