with. devtools(component보면서 할거다!)
=> 여기서의 onCreate 함수는 다시 생성이 되어도 똑같음
🙋♀️why? 비원시타입의 자료형 비교(객체등) 얕은 비교로 일어나기때문에 결론적으로 prop으로 받고있는 onCreate 함수는 랜더링 될때마다 계속 다시 만들어져서 onCreate를 prop으로 전달함
=> 즉 우리가 해야할거: onCreate함수가 재생성되지 않아야한다.
🙋♀️useMemo 써서 두번째 인자에 원하는 시점 값을 넣어서 사용하면 안되나?
=> 안됨!
usememo는 함수 반환이 아니라, 값을 반환해서 onCreate함수를 원본 그대로 보내주는게 안됨!!
=> 값 반환이 아니라 메모이제이션된 콜백을 반환!
=> 빈 배열을 넣으면 나오는 문제: 일기 저장하기 누르면 하나만 저장됨
🙋♀️onCreate는 컴포넌트가 마운트되는 시점 한번만! 수행하기때문에 처음값만 들어가는것임
import { useMemo, useEffect, useRef, useState, useCallback } from "react";
import "./App.css";
import DiaryEditor from "./DiaryEditor";
import DiaryList from "./DiaryList";
function App() {
// app -> editor 적용할 데이터
//랜더링 두번 일어나는 이유: 1️⃣ 초기값은 빈 배열
const [data, setData] = useState([]); // 빈 배열, 앞으로 채워넣을것임
const dataId = useRef(0);
const getData = async () => {
const url = "https://jsonplaceholder.typicode.com/comments";
const res = await fetch(url).then((res) => res.json());
//json값만 뽑아오는것(promise객체로 리턴하면서 resolve값을 json()사용해서 변수 res에 담아라!), then은 무조건 들어야하는것임
// console.log(res);
/* 이렇게도 쓸수있음 */
// const url = "https://jsonplaceholder.typicode.com/comments";
// const res2 = await fetch(url);
// const response = await res2.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까지)
setData(initData); //3️⃣완성된 결과 setData에 저장, dataState바뀜(두번 랜더링)
};
//실행할 시점: app 컴포넌트가 마운트 되자마자!
useEffect(() => {
getData(); //2️⃣마운팅 일어난 시점, 데이터 만들고
}, []);
// 전달해줄 함수 onCreate
const onCreate = useCallback((author, content, emotion) => {
const created_date = new Date().getTime();
const newItem = {
author, // 비구조화 할당맞다, 키랑 이름 맞추면 그대로 들어감
content,
emotion,
created_date,
id: dataId.current,
};
dataId.current += 1; //current에 1 더해줘야함
setData((data) => [newItem, ...data]); //
}, []); // 빈배열말고 데이터들을 다 넣어줘야함, 그러면 값은 다 나오지만 계속 랜더링은됨(그럼 setData에 콜백함수를 전달하고 뎁스 비우기)
// 삭제해줄 함수 onRemove , 어떤 아이디를 갖고있는 애를 지우면 됨!
const onRemove = (targetId) => {
const newDiaryList = data.filter((it) => it.id !== targetId); //다른것만 뉴배열에 넣어라~
console.log(newDiaryList); // 기억하기!! setData함수 안쓰면 삭제는 진행 안됨!
setData(newDiaryList);
}; // 얘를 지우려면, DiaryItem에 가서 지워야함(컴포넌트 전달은 부모인 DiaryList에게!)
// 수정해줄 함수 onEdit
const onEdit = (targetId, newContent) => {
//targetId, newContent 받아가지고와서 => 그 아이디 일치하는 애만 바꿔주면됨
//데이터 바꾸려면 ? setData()사용~
//새로운 데이터를 넘겨줘야함!
setData(
data.map(
(it) => (it.id === targetId ? { ...it, content: newContent } : it)
// ...it 들어온 데이터 뿌려주고, content만 새로운 content로!
)
);
};
// 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} />
{/* 4️⃣ 부모가 랜더링 되기때문에 , 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;