setState는 비동기이다.
dispatch({type:CLICK_CELL});
dispatch({type:CHANGE_TURN});
console.log(state.turn); //바뀐 turn값이 나오지 않는다.
원래 React의 setState와 useReducer는 state가 다 비동기적으로 바뀐다.
(Redux는 동기적으로 바뀜)
그래서 setState나 dispatch를 한 다음에 바로 state를 console.log로 출력해보면 의도한 값이 안나올 수도 있다.
※ 따라서 스테이트를 변경한 후 뭔가 처리할 때는 useEffect를 사용해야한다.
(dispatch나 setState를 한 다음 바로 해당 state를 사용하는 로직이 나타나면 의도한 값이 아닐 수 있어서 에러 발생 가능성이 높다.)
강의에서는 테이블 클릭 이벤트 ▶ CLICK_CELL 액션 생성 ▶ dispatch 호출 ▶ reducer 호출 ▶tableData, recentCell state변경 ▶ useEffect에서 변경된 tableData를 기반으로 승리자가 있는지 검사 할 때 사용했다.
useEffect(()=>{
const [row,cell] = recentCell;
if(row < 0) return ; //최초로 랜더링 될 때 실행되는 것 방지
//승리자가 있는지 검사
//무승부 검사
//턴변경
...
},[state.recentCell]);
강의에서의 비동기 state변경 문제
강의 내용 중에 dispatch가 연달아 발생하는 곳이 있는데 여기서 문제가 발생한다.
CLICK_CELL을 dispatch 하면 reducer에서는 CLICK_CELL에 해당하는 로직이 수행되는데 그 와중에 바로 CHANGE_TURN까지 같이 되어버려서 CLICK_CELL이 제대로 작동하지 않는 현상이 있었다. (CLICK_CELL로직 중에 state.turn을 사용하는 곳이 있었음. 원래 O가 들어가야하는데 X가 들어가는 문제가 생김. 클릭한 당시의 나의 턴이 아닌 상대방 턴으로 넘어가버림.)
이게 setState는 모아서 처리하는 것 처럼 dispach도 모아서 한번에 처리하면서 그렇게 된건가? 싶기도함..
여기서 정말 다시 강조하심..
제일 중요 : 디스패치가 스테이트를 바꾸는게 비동기이다.
정리 - 왜 useReducer를 사용하는가?
[state관리의 용이성]
- 이전에는 state를 여러 개 만들었는데 그 state들이 나중에 10개, 100개가 될 수가 있다.
- 관리해야하는 변수들이 너무 많아지고 setTable, setTableData, setTurn 이러한 것들이 너무 많아져서 가독성을 헤칠수도 있다.
- 그래서 한번에 state를 모아서 처리하는 것이 필요하다. (initialState를 객체로 만드는 이유)
[setState 함수 관리의 용이성]
- state를 한번에 모아서 관리하므로 setState함수들도 한방에 처리해주는 것도 필요함. 그것이 dispatch, action, reducer의 역할이다.
React가 Redux의 개념을 도입해서 useReducer가 생기게 된 것.
정리 - useReducer 실행순서
- 한번에 모아둔 state들은 액션을 통해서만 변경한다.
- 이벤트 발생
- action 생성 (action type == action name)
- action 실행 (dispatch)
- reducer가 action을 수행함. == 우리가 정의해둔 대로 state를 바꾼다.
- state를 바꿀 때는 불변성을 지키는 것이 매우 중요!