프로젝트의 컴포넌트와 데이터 구조를 생각해보자
<App />
-> <DiaryEditor />
<App />
-> <DiaryList />
App
컴포넌트가 부모, DiaryEditor
가 DiaryList
컴포넌트에게 일기 아잉템을 추가해주는게 아니고 State
를 App
컴포넌트가 관리하는 형식이다.
조금 더 구체화하면, 아래와 같다.
App
[data,setData] -> setData
-> DiaryEditor
컴포넌트
App
[data,setData] -> Data
-> DiaryEditor
컴포넌트
Data는 부모에서 자식으로, Event는 자식에서 부모로 진행하는 방식이다.
이 방식을 사용하여 App.js에 데이터 형태를 만들어준뒤 데이터를 아래로 이벤트에 해당하는 결과를 위로 전달받는 형태로 만들어보자.
배열로 초기값을 만들어주고, 자식에게 내려줄 data
, setData
를 생성한다.
DiaryEditor
에게 setState
를 전달하기 위해 onCreate
함수를 만들어서 전달해주고, DiaryList
에게 data를 전달하기 위해 {data}를 prop
으로 전달해준다.
const [data,setData] = useState([]);
const onCreate = (author, content, emotion) => {
const created_date = new Date().getTime();
const newItem = {
author,
content,
emotion,
created_date,
id: dataId.current,
};
dataId.current += 1;
setData([newItem, ...data]);
};
return(
<div className="App">
<DiaryEditor onCreate={onCreate} />
<DiaryList diaryList={data} />
</div>
);
DiaryEditor.js
에 onCreate
함수를 매개변수로 전달해주고 handleSubmit
함수에 onCreate
함수에 author, content, emotion
을 매개변수로 전달해서 setData
에 값을 추가해주고 setState
는 초기화해준다.
const DiaryEditor = ({ onCreate }) => {
const authorInput = useRef();
const contentInput = useRef();
const [state, setState] = useState({
author: "",
content: "",
emotion: 1,
});
const handleChangeState = (e) => {
console.log(e.target.name);
console.log(e.target.value);
setState({
...state,
[e.target.name]: e.target.value,
});
};
const handleSubmit = () => {
//focus
if (state.author.length <= 1) {
authorInput.current.focus();
return;
}
// focus
if (state.content.length < 5) {
contentInput.current.focus();
return;
}
onCreate(state.author, state.content, state.emotion);
console.log(state);
alert("저장 성공");
setState({
author: "",
content: "",
emotion: 1,
});
};
삭제를 하는 경우
App.js
에 Id를 매개변수로 하는 onRemove
함수를 만들어준다. 추가로 삭제된 아이템을 제외한 아이템들을 리스트에 넣어서 setData
해줘 DiaryList.js
에 전달해준다.
const onRemove = (targetId) => {
console.log(`${targetId}가 삭제되었습니다.`);
const newDiaryList = data.filter((it) => it.id !== targetId);
setData(newDiaryList);
};
return{
<div className="App">
<DiaryEditor onCreate={onCreate} />
<DiaryList onRemove={onRemove} diaryList={data} />
</div>
);
DiaryList.js
에 onRemove
함수를 prop
으로 전달해주고 마찬가지로 DiaryItem
에도 onRemove
를 전달해준다.
const DiaryList = ({ onRemove, diaryList }) => {
console.log(diaryList);
return (
<div className="DiaryList">
<h2>일기 리스트</h2>
<h4>{diaryList.length}개의 일기가 있습니다.</h4>
<div>
{diaryList.map((it) => (
<DiaryItem key={it.id} {...it} onRemove={onRemove} />
))}
</div>
</div>
);
};
DiaryItem.js
에서 삭제버튼을 누르면 handleRemove
함수가 실행되도록 설정해준다.
onst DiaryItem = ({
onRemove,
author,
content,
emotion,
created_date,
id,
}) => {
const handleRemove = () => {
console.log(id);
if (window.confirm(`${id}번째 일기를 정말 삭제하시겠습니까?`)) {
onRemove(id);
}
};
return (
<div className="DiaryItem">
<div className="info">
<span>
작성자:{author} | 감정점수:{emotion}
</span>
<br />
<span className="date">{new Date(created_date).toLocaleString()}</span>
</div>
<div className="content">
</div>
<button onClick={handleRemove}>샥제하기</button>
</div>
);
};
export default DiaryItem;
수정하는경우
App.js
에 추가, 삭제 경우와 마찬가지로 수정 함수를 만들어준다.
const onEdit = (targetId, newContent) => {
setData(
data.map((it) =>
it.id === targetId ? { ...it, content: newContent } : it
)
);
};
return (
<div className="App">
<DiaryEditor onCreate={onCreate} />
<DiaryList onEdit={onEdit} onRemove={onRemove} diaryList={data} />
</div>
);
DiaryList
에 onEdit
을 prop
으로 전달해주고 DiaryItem
에도 마찬가지로 전달해준다.
const DiaryList = ({ onEdit, onRemove, diaryList }) => {
console.log(diaryList);
return (
<div className="DiaryList">
<h2>일기 리스트</h2>
<h4>{diaryList.length}개의 일기가 있습니다.</h4>
<div>
{diaryList.map((it) => (
<DiaryItem key={it.id} {...it} onRemove={onRemove} onEdit={onEdit} />
))}
</div>
</div>
);
};
DiaryItem.js
import { useState, useRef } from "react";
const DiaryItem = ({
onEdit,
onRemove,
author,
content,
emotion,
created_date,
id,
}) => {
const [isEdit, setIsEdit] = useState(false);
const toggleIsEdit = () => {
setIsEdit(!isEdit);
};
const [localContent, setLocalContent] = useState(content);
const localContentInput = useRef();
const handleRemove = () => {
console.log(id);
if (window.confirm(`${id}번째 일기를 정말 삭제하시겠습니까?`)) {
onRemove(id);
}
};
const handleQuitEdit = () => {
setIsEdit(false);
setLocalContent(content);
};
const handleEdit = () => {
if (localContent.length < 5) {
localContentInput.current.focus();
return;
}
if (window.confirm(`${id}번째 일기를 수정하시겠습니까?`)) {
onEdit(id, localContent);
toggleIsEdit();
}
};
return (
<div className="DiaryItem">
<div className="info">
<span>
작성자:{author} | 감정점수:{emotion}
</span>
<br />
<span className="date">{new Date(created_date).toLocaleString()}</span>
</div>
<div className="content">
{isEdit ? (
<>
<textarea
value={localContent}
onChange={(e) => setLocalContent(e.target.value)}
/>
</>
) : (
<>{content}</>
)}
</div>
{isEdit ? (
<>
<button onClick={handleQuitEdit}>수정 취소</button>
<button onClick={handleEdit}>수정 완료</button>
</>
) : (
<>
<button onClick={handleRemove}>샥제하기</button>
<button onClick={toggleIsEdit}>수정하기</button>
</>
)}
</div>
);
};
export default DiaryItem;