전에 더미 데이터를 선언해주고 그 데이터만 일기 리스트에 보이게 만들었었다.
이번에는 이 더미 데이터를 없애주고, 직접 일기를 입력해준 내용이 나오게 만들어줘본다.
부모 컴포넌트인 App 에서 data를 관리하는 state 함수를 만들어주고 일기를 입력 받는 자식 컴포넌트에게는 해당 입력 필드의 값을 읽어서 데이터를 추가해줄 수 있는 setData가 포함된 함수를 넣어주고
일기를 보여주는 자식 컴포넌트에는 입력 받은 data 자체를 보내줘서 관리해준다.
function App() {
const [data,setData] = useState([]);
const dataId = useRef(0);
const onCreate = (author, content, emotion) => {
const created_date = new Date().getTime();
const newItem = {
id : dataId.current,
author,
content,
emotion,
created_date,
}
dataId.current += 1;
setData([newItem, ...data]);
}
return (
<div>
<DiaryEditor onCreate={onCreate} />
<DiaryList diaryList={data} />
</div>
);
}
삭제하기
이미 관리되고 있는 data 는 배열 형태므로 삭제하려는 item에 삭제 버튼을 만들어서,
id 정보를 가져와서 배열 filter() 메서드를 사용하여 준다.
다만, App -> DiaryList -> DiaryItem 까지 두 단계를 거쳐서 Props 를 넘겨줘야 한다.
// App.js
const onDelete = (id) => {
const newData = data.filter((e, idx) => idx !== id);
setData(newData);
}
export default function DiaryItem({ author, content, emotion, created_date, id, onDelete}) {
return (
<div className='DiaryItem'>
<div className='info' key={id}>
<span>작성자 : {author} | 감정 점수 : {emotion}</span>
<br/>
<span className='date'>{new Date(created_date).toLocaleString()}</span>
</div>
<div className='content'>{content}</div>
<button onClick={() => {
if(window.confirm(`${id}번 일기를 정말 삭제하시겠습니까?`))
onDelete(id);
}}>삭제하기</button>
</div>
)
}
window.confirm() 함수는 예/아니오 창을 띄우고 boolean 값을 리턴해준다
삭제는 id 값만 읽어와서 원래 data 에서 삭제해주면 되므로 어렵지 않았으나...
글 수정하기
조금 복잡하다...
const onEdit = (id, newContent) => {
setData(
data.map((e) => e.id === id ?
{...e, content: newContent}
: e)
);
}
App.js 에 만들어준 수정 함수로 setData 를 사용하여 DiaryItem 에서 수정된 값을 newContent 로 가져와서
변경해주는 id와 동일한 경우 원래 data 내용 뒤에 { ...e, content: newContent } 로 바뀐 내용을 넣어준다.
DiaryItem 에선 글 수정을 위하여 2개의 State 를 지정해주는데,
수정 모드인지 아닌지 검사해주는 boolean state 와
수정 전후 값에 대한 state 인 localContent
전통적으로 수정 모드인지 아닌지 검사해주는 state 인 isEdit은 false 로 두었다가, 변화가 필요할 때
const [isEdit, setIsEdit] = useState(false);
const toggleIsEdit = () => setIsEdit(!isEdit);
!isEdit 을 setIsEdit() 으로 수정해서 토글 기능을 수행하게 만든다.
또한 수정하기 버튼을 누르면 -> 원래 글의 컨텐츠 부분이 인풋 필드로 변경되면서 글의 원래 내용도 나와있어야 함
-> 버튼이 수정취소와 수정완료 로 바뀌어야 한다.
<div className='content'>
{isEdit ?
<textarea
value={localContent}
onChange={(e) => setLocalContent(e.target.value)}
ref={localContentInput}
/>
:
<>{content}</>
}
</div>
{isEdit ?
<>
<button onClick={handleQuitEdit}>수정취소</button>
<button onClick={handleEdit}>수정완료</button>
</>
: <>
<button onClick={handleRemove}>삭제하기</button>
<button onClick={toggleIsEdit}>수정하기</button>
</>
}
수정 모드 state 인 isEdit 과 3항 연산자로 응용
수정 모드일 때 textarea 의 value 는 새로 입력 받는 localContent 가 되어야 하며, 이는 setLocalContent() 로
입력되는 값에 따라 (e.target.value) 변경된다.
수정 모드에서 수정취소의 경우, setIsEdit() 을 false, setLocalContent()는 원래 content 값으로 해주면 된다.
수정 완료의 경우에는 App.js 에서 가져온 onEdit() 함수와 더불어 toggleIsEdit() 을 재실행 해줘서 리렌더링해줘야 한다.
export default function DiaryItem({
author, content, emotion, created_date, id, onRemove, onEdit
}) {
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' key={id}>
<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)}
ref={localContentInput}
/>
:
<>{content}</>
}
</div>
{isEdit ?
<>
<button onClick={handleQuitEdit}>수정취소</button>
<button onClick={handleEdit}>수정완료</button>
</>
: <>
<button onClick={handleRemove}>삭제하기</button>
<button onClick={toggleIsEdit}>수정하기</button>
</>
}
</div>
)
}
Lifecycle Methods
생성 Mount : 화면에 나타나는 것
- componentDidMount
변화 Update : 리렌더 하는 것 -> state, props 변경
- componentDidUpdate
제거 UnMount : 화면에서 사라짐
- componentWillUnmount
---- 위의 생명주기 메서드는 클래스형 컴포넌트에서만 사용 가능
함수형 컴포넌트에서는?
useEffect 라는 훅을 사용하여 가능하다!
useEffect(콜백함수, deps-의존성 배열)
useEffect(() => {
}, []);
// Mount 되는 순간만 콜백함수 실행
useEffect(() => {
console.log('Mount');
}, []);
// 컴포넌트가 리렌더링 될 때마다 실행
useEffect(() => {
console.log('Update');
});
// 특정 상태가 리렌더링 될 때마다 실행
useEffect(() => {
console.log(`count is update : ${count}`);
}, [count]);
useEffect(() => {
console.log(`text is update : ${text}`);
}, [text]);
// 컴포넌트가 사라질 때 실행 - useEffect() 안에 return 으로 함수를 리턴해주면 된다.
useEffect(() => {
return () => {
console.log('Unmount');
}
}, []);
const UnmountText = () => {
useEffect(() => {
console.log('Mount');
return () => {
console.log('Unmount');
}
}, []);
return (
<div>Unmount Testing Component</div>
)
}
export default function Lifecycle() {
const [isVisible, setIsVisible] = useState(false);
const toggle = () => setIsVisible(!isVisible);
return (
<div style={{padding: '20px'}}>
<button onClick={toggle}>On/Off</button>
{isVisible && <UnmountText/>}
</div>
)
}
해당 코드는 토글 버튼을 눌러서, 컴포넌트를 보였다가 사라졌다가 해주는 예제인데,
이때 isVisible 이 true 인 상태에만 보이게끔 하는 방식은 { isVisible && <컴포넌트명 /> } 형태이다.
단락 회로 평가? 에 의하여 false 인 경우는 뒤의 조건은 보지도 않기 때문에 토글 기능을 구현해줄 수 있다.
위의 방식으로 UnmountText 컴포넌트를 조절하여, useEffect() 로 Mount 와 Unmount 를 출력해볼 수 있다.
'Web Study > 한입크기로잘라먹는리액트' 카테고리의 다른 글
한입 크기로 잘라먹는 리액트 - 5 (0) | 2023.03.31 |
---|---|
한입 크기로 잘라먹는 리액트 - 3 (0) | 2023.03.29 |
한입 크기로 잘라먹는 리액트 - 2 (0) | 2023.03.27 |
한입 크기로 잘라 먹는 리액트 - 자바스크립트 기초편 (0) | 2023.03.22 |