📌 일기장 만들기 (6) - UPDATE
☑️ UPDATE
- 리스트 데이터를 동작으로 수정하기
- 수정하기(수정취소/수정완료) 기능 추가
🛠️ 복습
- 삭제하기 함수명, Props 수정(onDelete > onRemove)을 통해서 복습
- App 컴포넌트 > DiaryList 컴포넌트 > DiaryItem 컴포넌트
- 컴포넌트 트리 전역에 공급되는 함수명, Props을 바꾸면 굉장히 불편할 수 있음 (나중에 해결방법 배울 예정)
// src/App.js
const onRemove = (targetId) => {
console.log(`${targetId}가 삭제되었습니다`);
const newDiaryList = data.filter((it) => it.id !== targetId);
//console.log(newDiaryList);
setData(newDiaryList);
};
<DiaryList onRemove={onRemove} diaryList={date} />
// src/DiaryList.js
const DiaryList = ({ onEdit, onRemove, diaryList }) => {
return (
<div className="DiaryList">
<h2>일기 리스트</h2>
<h4>{diaryList.length}개의 일기가 있습니다.</h4>
<div>
{diaryList.map((it) => (
<DiaryItem key={it.id} {...it} onEdit={onEdit} onRemove={onRemove} />
))}
</div>
</div>
);
};
// src/DiaryItem.js
const DiaryItem = ({
onRemove, id, author, content, emotion, created_date }) => {
☑️ 수정 기능 버튼 만들기
- 일기를 작성하면 수정 기능 버튼이 생성되고, 일기의 내용을 변경할 때 수정 폼과 수정 취소/완료로 버튼이 변경되도록 만들기
☑️ handleRemove 함수 생성
- 삭제하기 onClick 이벤트 이동 (return 위로 이동)
- 삭제하기 onClick 이벤트 넣고, 삭제하기 button에 Props으로 전달
☑️ 수정하기 상태 추가
- useState import
- useState default는 false
- isEdit state : boolean(t/f) 값, 수정중인지? 수정중이지 않은지?의 데이터 보관
- toggleIsEdit 함수 : 호출이 되는 순간 isEdit 값을 반전시킴 (not 논리연산), 수정하기 버튼에 Props으로 전달
const [isEdit, setIsEdit] = useState(false);
const toggleIsEdit = () => setIsEdit(!isEdit);
☑️ 수정하기 버튼 클릭 시 수정 폼 생성하기 추가
- content devision 수정
- isEdit 삼항연산자로 True/False일 때 렌더링될 것을 작성
- True일 때는 textarea(수정 작성 폼)
- False일 때는 버튼을 누리지 않은 상태로 돌아감
☑️ 수정 폼 관리 - state로 핸들링하기
- localContent(textarea의 value) : 수정 폼으로 변경 시
- setLocalContent (target.value) : 수정 폼 상태 변경 시
- 수정하지 않는 경우면 원래대로 content
const [localContent, setLocalContent] = useState();
const localContentInput = useRef();
<div className="content">
{isEdit
? <><textarea ref={localContentInput}
value={localContent}
onChange={(e) => setLocalContent(e.target.value)} /></>
: <>{content}</>
}
</div>
☑️ 수정하기 버튼 클릭 시 수정취소/수정완료 버튼으로 렌더링
- handleQuitEdit 수정 취소, handleEdit 수정 완료
- 아닌 경우 원래 버튼으로(삭제하기, 수정하기)
{isEdit
? <>
<button onClick={handleQuitEdit}>수정 취소</button>
<button onClick={handleEdit}>수정 완료</button>
</>
: <>
<button onClick={handleRemove}>삭제하기</button>
<button onClick={toggleIsEdit}>수정하기</button>
</>
}
☑️ 수정 폼이 나타났을 때 원본 데이터가 들어오도록 기능 추가
- 수정 폼을 관리하는 localContent를 원래 content로 바꾸기 (수정하기 이전 상태로 자동으로 나옴)
const [localContent, setLocalContent] = useState(content);
☑️ 수정 폼 초기화 적용
- localContent가 훼손되도 초기화됨
const handleQuitEdit = () => {
setIsEdit(false); // false
setLocalContent(content); // 원래대로
};
☑️ 일기 아이템이 진짜 수정되서 저장되도록 적용
- 리액트 특성 (EVENT는 위로, DATA는 아래로)
- 수정 완료 이벤트는 DiaryItem 컴포넌트에서 App 컴포넌트로 가야함
- Data를 가지고 있는 App 컴포넌트에 수정하는 기능을 넣고, DiaryItem 컴포넌트로 보내주기
- onEdit 함수 : 수정하기
src/App.js
// 어떤 id를 가진 일기를 수정할 것인지(targetId) 수정된 데이터가 뭔지(newContent)
// onEdit 특정 일기 데이터를 수정하는 함수
const onEdit = (targetId, newContent) => {
// setData를 통해서 어떤 값을 전달한다
setData(
// 변경 시키는 값 만들기
// 원본데이터에 map 내장함수 사용
// 모든 요소(it)을 순회한 후 새로운 배열을 만들어서 setData에 저장
// 새로운 배열은? 수정이 완료된 배열을 전달해야 한다
data.map( (it) =>
// 수정대상이라면?
it.id === targetId
// 컨텐츠가 뉴컨텐츠로 바뀜(수정된 배열)로 교체해줄 것인지
? { ...it, content: newContent }
// 수정대상이 아니면 원래있던 배열을 다시 반환할 것인지 (원본값)
: it
)
);
};
☑️ onEdit 함수 DiaryList 컴포넌트에 전달
src/App.js
return (
<div className="App">
<DiaryEditor onCreate={onCreate} />
<DiaryList onEdit={onEdit} onRemove={onRemove} diaryList={data} />
</div>
);
☑️ DiaryList 컴포넌트는 Prop으로 전달
src/DiaryList.js
const DiaryList = ({ onEdit, onRemove, diaryList }) => {
return (
<div className="DiaryList">
<h2>일기 리스트</h2>
<h4>{diaryList.length}개의 일기가 있습니다.</h4>
<div>
{diaryList.map((it) => (
<DiaryItem key={it.id} {...it} onEdit={onEdit} onRemove={onRemove} />
))}
</div>
</div>
);
};
☑️ DiaryItem 컴포넌트는 DiaryList를 통해 전달받기
- 입력 정확하게 하도록 일기 5글자 이상 작성 하도록 focus 추가 (useRef import)
- localContentInput 함수 usRef (textarea에도 추가)
- 수정 완료 버튼 이벤트 추가 (id값과 새로 바뀌는 localContent 전달)
- 삭제 전 dialog 추가(몇번째 일기 삭제할 것인지 alert으로 묻기)
- 수정 완료 시 수정 폼(textarea) 닫아주기 (toggleIsEdit)
src/DiaryItem.js
import { useRef, useState } from "react";
const DiaryItem = ({
onEdit, onRemove, id, author, content, emotion, created_date
}) => {
const [isEdit, setIsEdit] = useState(false);
const toggleIsEdit = () => setIsEdit(!isEdit);
const [localContent, setLocalContent] = useState(content);
const localContentInput = useRef();
const handleRemove = () => {
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 className="author_info">
| 작성자 : {author} | 감정점수 : {emotion} |
</span>
<br />
<span className="date">{new Date(created_date).toLocaleString()}</span>
</div>
<div className="content">
{isEdit
? <><textarea ref={localContentInput} 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;
☑️ 결과
📖 참조하기
컴포넌트 설계 구조와 데이터 전달 흐름
출처 - https://velopert.com/3528
출처 - https://velog.io/@youthfulhps/What-is-Redux-and-why-use-it복잡성
출처 - 생활코딩
'React > React 공부' 카테고리의 다른 글
일기장 만들기 (9) API 호출하기 : useEffect x fetch (0) | 2023.02.11 |
---|---|
Lifecycle 제어 : useEffect (0) | 2023.02.11 |
React 기본 - 일기장 만들기(5) [CRUD : DELETE] (0) | 2023.02.09 |
React 기본 - 일기장 만들기(4) [CRUD : CREATE] (0) | 2023.02.09 |
React 기본 - 일기장 만들기(3) [CRUD : READ] (0) | 2023.02.09 |