완성된 프로젝트 업그레이드
1. 복잡한 상태 변화 로직을 컴포넌트로부터 분리하기
(App.js)
onCreate, onEdit, onRemove 함수는 data를 참조해야 하기 때문에 컴포넌트 바깥으로 뺄 수 없었음
But, 컴포넌트가 무거워지는 건 안 좋음
=> useReducer : React의 상태 관리를 돕는 훅
상태변화 로직들을 컴포넌트에서 분리할 수 있음
(useState의 대체 함수)
ex)
useState만 사용하면 상태변화 함수를 모두 Counter 컴포넌트 안에 작성해야 함 (count를 참조하기 때문) -> Counter 컴포넌트 복잡해짐
useReducer 사용하면 상태변화 로직을 컴포넌트 외부에 작성
useReducer 사용법
- state: 데이터, 초기값, 바뀌는 값
- event: state 변경
- dispatch: 액션을 발생시키는 함수
- action: 수정할 data, 객체 형식
- reducer 함수: action을 어떻게 바꿀지 (주로 switch 이용)
이전 state와 action을 받아서 다음 state를 돌려주는 함수
dispatch(action) 함수 안에 파라미터로 액션값을 넣으면 reducer 함수가 호출되는 구조
dispatch는 함수형 업데이트 필요X (호출하면 알아서 현재의 state를 reducer 함수가 참조함 -> useCallback 사용 시 의존성 배열 고려할 필요X)
- reducer 함수 작성
const reducer = (state, action) => { // reducer는 두가지 인자를 받는다. // state : 현재 (reducer실행 전의) state값 // useReducer의 dispatch의 파라미터인 action으로 전달된 값 // switch 사용 예제 switch (action.type) { case TYPES.CASE_1: // case1에서 변경할 과정들을 실행하고 변경할 state를 return한다. return ; case TYPES.CASE_2: // case1에서 변경할 과정들을 실행하고 변경할 state를 return한다. return ; default: rdturn state; // switch문 내에서 return한 값들이 state값으로 변경 된다! } }
- useReducer 사용
const [state, dispatch] = useReducer(reducer, state초기값) const handleDispatch = () => { dispatch({ type: CASE_1(실행할 작업을 설명할수 있도록 작성 또는 그 값을 상수화 한 객체 등을 사용), payload: '변경할 값' }) }
<div onClick={handleDispatch}> 리듀서 호출 </div>
useReducer의 묘미는,
⭐️한 컴포넌트 내에서 State를 업데이트하는 로직 부분을 그 컴포넌트로부터 분리시키는 것을 가능⭐️하게 해준다는 것이다.
'분리시키는 것을 가능하게 해준다'는 것은,
State 업데이트 로직을 또다른 파일에 작성해서 (분리), 분리된 파일을 불러와서 사용하는 것도 가능하다는 뜻도 된다.
(1)useState를 사용한 경우 : 컴포넌트 내부에 State 업데이트 로직이 존재
(2)useReducer를 사용한 경우 : 컴포넌트 외부에 State 업데이트 로직이 존재
참조: https://velog.io/@iamhayoung/React-Hooks-useReducer%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0
redux와의 차이점
redux: state가 동기적으로 바뀜
useReducer: state가 비동기적으로 바뀜
실습
(App.js)
1. useState를 useReducer로 바꾸기const [data, dispatch] = useReducer(reducer, []);
- reducer 함수 만들기
const reducer = (state, action)=>{ switch(action.type){ case: 'INIT': case: 'CREATE': case: 'REMOVE': case: 'EDIT': default : return state; // 잘못 전달되면 상태를 변화시키지 않도록 }; };
- reducer 사용
App컴포넌트에서 setData가 했었던 역할을 dispatch와 reducer에 나눌 것
1) 'INIT' 데이터 초기화//reducer case 'INIT': { return action.data; }
2) 'CREATE'created_date는 reducer에서 만들어서 사용하자
//reducer case 'CREATE': { const created_date = new Date().getTime(); const newItem = { ...action.data, created_date, }; return [newItem, ...state]; }
3) 'REMOVE'
//reducer case 'REMOVE': { return state.filter(it => it.id !== action.targetId); }
4) 'EDIT'
//reducer case 'EDIT': { return state.map(it => it.id === action.targetId ? { ...it, content: action.newContent } : it, ); }
(전체 코드)
(App.js)