배열에 직접 추가하고 삭제하는 것이 아님
=> 기존 data에 새 데이터를 합치거나 삭제 데이터를 빼준 데이터를 새로운 변수에 담아서 setData를 통해 다시 셋팅(업데이트)함
알게된 것
- props 넘기고 사용하기 (+한단계 더 안에있는 컴포넌트까지 props전달)
- useRef() 활용
- filter 활용
- state를 toggle(반전연산)함수로 만들어 사용
컴포넌트&데이터 구조
- 초기값
data = [item1]
<DiaryEditor props={setData}/>
setData를 전달하여 DiaryEditor에서 새로운 일기 작성을 하면 App컴포넌트의 data에 추가가 된다. =>data = [item1, item2]
- 추가된 data를
<DiaryList props={data} />
로 전달하여 리스트에 data를 적용
DiaryList 컴포넌트 생성 후 props로 리스트를 넘겨서 사용할 수 있다.
//DiaryList.js
const DiaryList = ({ diaryList }) => {
return (
<div className="DiaryList">
<h2>일기 리스트</h2>
<h4>{diaryList.length}개의 일기가 있습니다.</h4>
<div>
{diaryList.map((item) => (
<div key={item.id}>
<div>작성자 : {item.author}</div>
<div>일기 : {item.content}</div>
<div>감정 : {item.emotion}</div>
<div>작성 시간 : {item.created_date}</div>
</div>
))}
</div>
</div>
)
}
DiaryList.defaultProps = {
diaryList: [],
}
diaryList.length 로 사용하고있는데 리스트가 없다면?
=> DiaryList가 undefined 라면 .defaultProps
를 사용하여 별도의 처리로 빈 배열을 갖도록 하여 버그가 생기지않도록 해준다
map사용 시 key값을 넘겨줘야 하는데 콜백함수에서 각 아이템의 index로 넘겨줄 수 있지만 나중에 수정, 삭제 시에 문제가 생길 수 있음
=> 데이터에 id값이 있다면 id를 사용할것을 추천!
데이터 리스트를 수정, 삭제를 DiaryList 컴포넌트 안에서 전부 구현하는게 아닌 컴포넌트를 분할해 주는게 좋다.
//DiaryList.js
const DiaryList = ({ diaryList }) => {
return (
<div className="DiaryList">
<h2>일기 리스트</h2>
<h4>{diaryList.length}개의 일기가 있습니다.</h4>
<div>
{diaryList.map((item) => (
<DiaryItem key={item.id} {...item}/>
))}
</div>
</div>
)
}
//DiaryItem.js
const DiaryItem = ({author, content, created_date, emotion, 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">{content}</div>
</div>
)
}
new Date().toLocaleStriong()
: 가져온 created_date(ms로 계산된 시간)을 'yyyy.mm.dd.time'로 나타내줌//App.js
function App() {
const [data, setData] = useState([]);
const dataId = useRef(0);
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>
);
}
dataId = useRef(0)
: useRef를 사용하여 dataId에 초기값 0인 변경가능한 값을 담아줌dataId.current
: useRef의 .current는 변경 가능한 값을 담고있음setData([newItem, ...data])
: 최신작성글을 앞으로 놓기 위해 newItem을 0인덱스에 놓고 전개연산자로 기존 data를 풀어줌//App.js
const onRemove = (targetId) => {
const newDiaryList = data.filter((item) => item.id !== targetId)
setData(newDiaryList)
}
return (
<div className="App">
<DiaryEditor onCreate={onCreate} />
<DiaryList onRemove={onRemove} diaryList={data}/>
</div>
);
//DiaryItem.js
const handleRemove = () => {
if(window.confirm(`${id}번째 일기를 정말 삭제하시겠습니까?`)) {
onRemove(id)
}
}
<button onClick={() => {handleRemove}}>삭제하기</button>
window.confirm()
: 예(true), 아니오(false)를 반환해주는 컨펌 창을 띄워줌data.filter()
: item의 id가 해당 아이템의 id가 아니면 newDiaryList로 새로운 배열을 만들어 줌setData()
: 새로운 배열을 업데이트 시켜줌//App.js
const onEdit = (targetId, newContent) => {
setData(
data.map((item) =>
item.id === targetId ? {...item, content: newContent} : item
)
)
}
return (
<div className="App">
<DiaryEditor onCreate={onCreate} />
<DiaryList onRemove={onRemove} diaryList={data} onEdit={onEdit}/>
</div>
);
//DiaryItem.js
const [isEdit, setIsEdit] = useState(false);
const toggleIsEdit = () => setIsEdit(!isEdit)
const [localContent, setLocalContent] = useState(content);
const localContentInput = useRef()
const handleQuitEdit = () => {
setIsEdit(false)
setLocalContent(content)
}
const handleEdit = () => {
if(localContent.length < 5) {
return localContentInput.current.focus();
}
if(window.confirm(`${id}번째 일기를 수정하시겠습니까?`)) {
onEdit(id, localContent)
toggleIsEdit()
}
}
//...생략
setIsEdit(!isEdit)
: toggleIsEdit을 실행할때마다 isEdit을 반대로 업데이트 함handleQuitEdit()
: 수정중일 때 수정을 취소하면서 !isEdit
상태로 바꾸며, localContent에 수정중이던 text를 기존 content로 다시 넣어줌onEdit()
: 컨펌창 예(true) 클릭 시 id와 localContent(수정된 텍스트)를 전달해줌공부하며 정리&기록하는 ._. 씅로그