일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 리터럴 타입
- 태그된 유니온
- 타입 좁히기
- Jest
- RTK Query
- 인터섹션
- TS
- app router
- React
- 무한 스크롤
- autosize
- map
- useAppDispatch
- 이분 검색
- Cypress
- 반공변성
- 투포인터
- 호이스팅
- SSR
- 공변성
- CI/CD
- CORS
- recoil
- tailwind
- 결정 알고리즘
- async/await
- ESlint
- webpack
- Promise
- dfs
- Today
- Total
짧은코딩
복잡한 상태 관리 로직 분리하기 본문
App 컴포넌트에는 굉장히 많은 상태들이 존재한다.
-상태 변화 처리 함수
onCreate: 데이터 생성
onEdit: 데이터 수정
onRemove: 데이터 삭제
이 3개의 상태 변화 로직이 있다.
함수들 안에 data가 많다. 이렇게 컴포넌트의 코드가 길어지고 무거워 지는 것은 좋지 않다.
=> 따라서 복잡하고 긴 상태 변화 로직을 컴포넌트 밖으로 분리해야한다.
useReducer
const [state, dispatch] = useReducer(reducer, initialState);
이렇게 있으면 왼쪽 첫번째 인자는 state이다. 그리고 두번째 인자는 상태를 변환시키는 엑션을 발생시키는 함수이다.
오른쪽 첫번째 인자에는 reducer가 있어야한다. dispatch에서 일어난 상태 변화를 reducer가 처리해준다. 그리고 두번째 인자는 초기값이다.
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
버튼을 누르면 dispatch가 호출이된다. 그러면서 type 객체를 전달한다. 이 객체를 action 객체라고 한다.
action은 상태 변화라고 생각하면 된다.
따라서 dispatch가 호출되면 상태변화가 일어나고 이 상태 변화는 reducer가 처리한다.
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
dispatch가 일어나면 첫번째 인자로 최신의 state를 받고, 두번째 인자로는 dispatch 안의 type인 action 객체를 받는다.
-최적화 한 App.js
const reducer = (state, action) => {
switch (action.type) {
case "INIT": {
return action.data;
}
case "CREATE": {
const created_date = new Date().getTime();
const newItem = {
...action.data,
created_date,
};
return [newItem, ...state];
}
case "REMOVE": {
return state.filter((it) => it.id !== action.targetId);
}
case "EDIT": {
return state.map((it) =>
it.id === action.targetId ? { ...it, content: action.newContent } : it
);
}
default:
return state;
}
};
const App = () => {
// const [data, setData] = useState([]);
const [data, dispatch] = useReducer(reducer, []);
const dataId = useRef(0);
const getData = async () => {
const res = await fetch(
"https://jsonplaceholder.typicode.com/comments"
).then((res) => res.json());
const initData = res.slice(0, 20).map((it) => {
return {
author: it.email,
content: it.body,
emotion: Math.floor(Math.random() * 5) + 1,
created_date: new Date().getTime(),
id: dataId.current++,
};
});
dispatch({ type: "INIT", data: initData });
};
useEffect(() => {
getData();
}, []);
const onCreate = useCallback((author, content, emotion) => {
dispatch({
type: "CREATE",
data: { author, content, emotion, id: dataId.current },
});
dataId.current += 1;
}, []);
const onRemove = useCallback((targetId) => {
dispatch({ type: "REMOVE", targetId });
}, []);
const onEdit = useCallback((targetId, newContent) => {
dispatch({ type: "EDIT", targetId, newContent });
}, []);
이 코드를 보면 App 컴포넌트 외부에 reducer 함수를 만들어 컴포넌트의 길이와 복잡도를 줄인 것이 핵심이다.
dispatch는 함수형 업데이트 필요없이 그냥 호출하면 알아서 현재의 state를 reducer 함수가 참조해줘서 useCallback을 사용하면서 dependency array를 걱정하지 않아도 된다.
'인프런, 유데미 > 한입 크기로 잘라 먹는 리액트' 카테고리의 다른 글
일기장 만들기 되짚어보기 (0) | 2022.06.14 |
---|---|
컴포넌트 트리에 데이터 공급하기 (0) | 2022.06.01 |
최적화4-삭제, 수정 최적화 (0) | 2022.05.29 |
최적화3-컴포넌트 최적화 (0) | 2022.05.29 |
최적화2-컴포넌트 재사용 (0) | 2022.05.28 |