[Project-심플다이어리] 리스트 렌더/추가/수정/삭제
배열에 직접 추가하고 삭제하는 것이 아님
=> 기존 data에 새 데이터를 합치거나 삭제 데이터를 빼준 데이터를 새로운 변수에 담아서 setData를 통해 다시 셋팅(업데이트)함
알게된 것
- props 넘기고 사용하기 (+한단계 더 안에있는 컴포넌트까지 props전달)
- useRef() 활용
- filter 활용
- state를 toggle(반전연산)함수로 만들어 사용
- 일기를 작성해주는 에디터컴포넌트와 리스트를 보여주는 리스트컴포넌트로 나뉜다.
- App컴포넌트에서 일기data를 관리한다
컴포넌트&데이터 구조
- 초기값
data = [item1]
<DiaryEditor props={setData}/>
setData를 전달하여 DiaryEditor에서 새로운 일기 작성을 하면 App컴포넌트의 data에 추가가 된다. =>data = [item1, item2]
- 추가된 data를
<DiaryList props={data} />
로 전달하여 리스트에 data를 적용
1. props전달
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를 사용할것을 추천!
item컴포넌트 분할
데이터 리스트를 수정, 삭제를 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 컴포넌트로 key와 item의 요소들을 props로 전달
- 전개연산자를 사용하여 요소들을 풀어서 가져갈 수 있음
//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'로 나타내줌
2. 아이템 추가
- onCreate함수를 DiaryEditor컴포넌트의 props로 전달
//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>
);
}
- 일기 리스트를 담고있을 data, 일기 리스트를 셋팅해줄 setData
dataId = useRef(0)
: useRef를 사용하여 dataId에 초기값 0인 변경가능한 값을 담아줌
- DiaryEditor에서 작성한 author, content, emotion을 인수로 전달 받아서 onCreate작업을 처리함
dataId.current
: useRef의 .current는 변경 가능한 값을 담고있음
=> 컴포넌트가 한번 업데이트 될 때 마다 담고있는 값에 +1을 해줌
setData([newItem, ...data])
: 최신작성글을 앞으로 놓기 위해 newItem을 0인덱스에 놓고 전개연산자로 기존 data를 풀어줌
- 그렇게 셋팅된 data를 diaryList컴포넌트로 넘겨준다
3. 아이템 삭제
- DiaryItem 컴포넌트에서 삭제버튼 생성
- 아이템 추가처럼 삭제함수(onRemove)를 DiaryList를 통해 DiaryList 컴포넌트로 전달
//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)를 반환해주는 컨펌 창을 띄워줌
- true면 1번에서 props로 받아온 id(해당 아이템의 id) onRemove의 인수로 넘겨줌
data.filter()
: item의 id가 해당 아이템의 id가 아니면 newDiaryList로 새로운 배열을 만들어 줌
=> 클릭한 요소의 id인 아이템만 제외하고 새로운 배열로 생성
setData()
: 새로운 배열을 업데이트 시켜줌
4. 아이템 수정
- DiaryItem 컴포넌트에 수정버튼 생성
- App.js에서 수정함수(onEdit)를 DiaryList를 통해 DiaryItem 컴포넌트로 전달
- 수정하고있을 때(isEdit상태) 보여지는 버튼도 다르게 변경
//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을 반대로 업데이트 함
- localContent로 수정할 일기 내용을 불러오고 저장함
handleQuitEdit()
: 수정중일 때 수정을 취소하면서 !isEdit
상태로 바꾸며, localContent에 수정중이던 text를 기존 content로 다시 넣어줌
onEdit()
: 컨펌창 예(true) 클릭 시 id와 localContent(수정된 텍스트)를 전달해줌
//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는 변경 가능한 값을 담고있음=> 컴포넌트가 한번 업데이트 될 때 마다 담고있는 값에 +1을 해줌
setData([newItem, ...data])
: 최신작성글을 앞으로 놓기 위해 newItem을 0인덱스에 놓고 전개연산자로 기존 data를 풀어줌- DiaryItem 컴포넌트에서 삭제버튼 생성
- 아이템 추가처럼 삭제함수(onRemove)를 DiaryList를 통해 DiaryList 컴포넌트로 전달
//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)를 반환해주는 컨펌 창을 띄워줌- true면 1번에서 props로 받아온 id(해당 아이템의 id) onRemove의 인수로 넘겨줌
data.filter()
: item의 id가 해당 아이템의 id가 아니면 newDiaryList로 새로운 배열을 만들어 줌
=> 클릭한 요소의 id인 아이템만 제외하고 새로운 배열로 생성setData()
: 새로운 배열을 업데이트 시켜줌
4. 아이템 수정
- DiaryItem 컴포넌트에 수정버튼 생성
- App.js에서 수정함수(onEdit)를 DiaryList를 통해 DiaryItem 컴포넌트로 전달
- 수정하고있을 때(isEdit상태) 보여지는 버튼도 다르게 변경
//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을 반대로 업데이트 함
- localContent로 수정할 일기 내용을 불러오고 저장함
handleQuitEdit()
: 수정중일 때 수정을 취소하면서 !isEdit
상태로 바꾸며, localContent에 수정중이던 text를 기존 content로 다시 넣어줌
onEdit()
: 컨펌창 예(true) 클릭 시 id와 localContent(수정된 텍스트)를 전달해줌
//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(수정된 텍스트)를 전달해줌공부하며 정리&기록하는 ._. 씅로그
Author And Source
이 문제에 관하여([Project-심플다이어리] 리스트 렌더/추가/수정/삭제), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@sseung-i/React-리스트-렌더추가수정삭제저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)