=> app컴포넌트는 코드의 길이가 길고, data를 계속 참조해서 써야하므로 함수 안에 onCreate, onEdit, onRemove가 들어가있어야했다!
import { useMemo, useEffect, useRef, useCallback, useReducer } from "react";
import "./App.css";
import DiaryEditor from "./DiaryEditor";
import DiaryList from "./DiaryList";
//복잡한 컴포넌트 로직을 간단하게 바꿔주기 위해서이므로, 밖에 선언! 3️⃣
const reducer = (state, action) => {
//언제나 두개의 매개변수 받아야!
//reducer함수에서 데이터 가공한다고 생각하자!
//dispatch부르는 부분에서는 전달만!
switch (action.type) {
case "INIT": {
return action.data;
}
case "CREATE": {
const created_date = new Date().getTime(); //이제 더이상 onCreate에서 받아오지않음
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 } //id 일치하면 나머지 펼쳐주고, 수정한애만(content) newcontent로 바꾸기
: it
);
}
default:
//reducer에는 상태 변화시킬 의무, 즉 return 필요
return state;
}
//reducer가 리턴하는 값이 data의 값이 된다.
//어떤 data의 값이 필요한지 둘러보기4️⃣
};
function App() {
// const [data, setData] = useState([]); 1️⃣ 주석처리
// 2️⃣useReducer만들어주고
const [data, dispatch] = useReducer(reducer, []); //dispatch= setState같은애인데 항상 dispatch라고 이름지어줘야함
//dispatch를 호출하면 reducer가 실행된다.
// 초기값은 useState때처럼 [] 빈배열!
const dataId = useRef(0);
const getData = async () => {
const url = "https://jsonplaceholder.typicode.com/comments";
const res = await fetch(url).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(),
//return바로 되어서 후에 +1할수없으니까
id: dataId.current++,
};
});
//res배열 500개여서 20개만 자른다(0~19) - slice
//Math.random() * 5 => 0~4까지 랜덤난수 생성 , floor이용해서 내림 +1 (1~5까지)
dispatch({ type: "INIT", data: initData });
//type으로 알아보니까 담아주고, 데이터를 또 다른 프로퍼티로 담아준다.
};
//실행할 시점: app 컴포넌트가 마운트 되자마자!
useEffect(() => {
getData();
}, []);
// 전달해줄 함수 onCreate
const onCreate = useCallback((author, content, emotion) => {
dispatch({
type: "CREATE",
data: { author, content, emotion, id: dataId.current },
});
// const created_date = new Date().getTime();
// const newItem = {
// author,
// content,
// emotion,
// created_date,
// id: dataId.current,
// }; 이거 대신, dispatch로 바로 데이터 받아서 가공해주게!
dataId.current += 1; //current에 1 더해줘야함
}, []);
// 삭제해줄 함수 onRemove , 어떤 아이디를 갖고있는 애를 지우면 됨!
const onRemove = useCallback((targetId) => {
dispatch({ type: "REMOVE", targetId }); //id만 전달해주면 reducer함수에서 가공
}, []); // 얘를 지우려면, DiaryItem에 가서 지워야함(컴포넌트 전달은 부모인 DiaryList에게!)
// 수정해줄 함수 onEdit
const onEdit = useCallback((targetId, newContent) => {
dispatch({ type: "EDIT", targetId, newContent });
}, []);
// useMemo실습: 좋은감정, 안좋은감정 나누기
const getDiaryAnalysis = useMemo(() => {
//좋은 일기 개수
const goodCount = data.filter((it) => it.emotion >= 3).length;
//안좋은 일기 개수
const badCount = data.length - goodCount;
//좋은 일기 비율
const goodCountRatio = (goodCount / data.length) * 100;
//전체일기를 화면에 찍어줄것임 => return필요, 여러개니까 객체로 리턴
return { goodCount, badCount, goodCountRatio };
}, [data.length]); //두번째 인자 배열에 전달한 값이 바뀔때만 다시 수행함!
const { goodCount, badCount, goodCountRatio } = getDiaryAnalysis; //비구조화할당 이용해서 리턴값 변수에 담아주고(키로 구분), 함수 호출
return (
<div className="App">
<DiaryEditor onCreate={onCreate} />
<div>전체일기:{data.length}</div>
<div>기분 좋은 일기 개수:{goodCount}</div>
<div>기분 나쁜 일기 개수:{badCount}</div>
<div>기분 좋은 일기 비율:{goodCountRatio}</div>
<DiaryList onRemove={onRemove} diaryList={data} onEdit={onEdit} />
{/* prop으로 전달함!(부모=> 자식컴포넌트로 데이터 전달!) */}
</div>
);
}
export default App;