[React] useReaducer
💎 상태 변화 로직 분리하기
- count: state
- dispatch: 상태를 변화시키는 액션을 일으키는 함수 -> type 프롭이 들어있는 객체를 전달
- reducer: 상태 변화를 처리해주는 함수
이 객체는 상태에 대한 객체이다.
total Code
import "./App.css";
import { useRef, useEffect, useMemo, useCallback, useReducer } from "react";
import DiaryEditor from "./DiaryEditor";
import DiaryList from "./DiaryList";
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) =>
TimeRanges.id === action.targetId
? { ...it, content: action.newContent }
: it
);
}
default:
return state;
}
};
function App() {
// const [data, setData] = useState([]);
const [data, dispatch] = useReducer(reducer, []);
const dataId = useRef(0);
/* 새로운 리스트 추가 */
const onCreate = useCallback((author, content, emotion) => {
dispatch({
type: "CREATE",
data: { author, content, emotion, id: dataId.current },
});
dataId.current += 1;
}, []);
/* 기존 추가된 리스트 삭제 */
const onRemove = (targetId) => {
dispatch({ type: "REMOVE", targetId });
};
/* 데이터 수정 */
const onEdit = (targetId, newContent) => {
dispatch({ type: "EDIT", targetId, newContent });
};
/* API 호출하기 */
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 });
};
// Mount 시점에 수행
useEffect(() => {
getData();
}, []);
/* emotion 점수 카운팅 */
const getDiaryAnalysis = useMemo(() => {
const goodCount = data.filter((it) => it.emotion >= 3).length;
const badCount = data.length - goodCount;
const goodRatio = (goodCount / data.length) * 100;
return { goodCount, badCount, goodRatio };
}, [data.length]);
const { goodCount, badCount, goodRatio } = getDiaryAnalysis;
return (
<div className="App">
<h1>일기장</h1>
<DiaryEditor onCreate={onCreate}></DiaryEditor>
<div className="diaryAnalysis">
<h2> 전체 일기 : {data.length}</h2>
<div>기분 좋은 일기 개수 : {goodCount}</div>
<div>기분 나쁜 일기 개수 : {badCount}</div>
<div>기분 좋은 일기 비율 : {goodRatio}% </div>
</div>
<DiaryList
diaryList={data}
onRemove={onRemove}
onEdit={onEdit}
></DiaryList>
</div>
);
}
export default App;
🔆 코드 분석
기존에 useState를 사용해서 state가 변경될 때마다 data를 변경시켜주어서 사용했다. 그래서 이렇게 사용한 함수가 onCreate, onRemove, onEdit, getData 가 있다.
하나의 컴포넌트에 여러개의 함수들이 역할을 한다는 것은 그닥 좋지 않으므로 useReducer를 사용해서 역할을 분리해주는 것이다. => then HOW?
기존 useState자리에
const [data, dispatch] = useReducer(reducer, []);
으로 변경시켜준다. 진행과정은 dispatch를 통해서 어떤 type인지 액션 객체가 reducer로 전달이 되고, 거기서 리턴된 값이 data로 넘어가는 형식이다.
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) =>
TimeRanges.id === action.targetId
? { ...it, content: action.newContent }
: it
);
}
default:
return state;
}
};
state는 현재 변경되기 전 state를 말하고, action은 action 객체를 말한다. 기존의 함수들에서 사용되었던 변수들을 action 객체를 통해서 전달해주면 된다.
댓글남기기