What I Learnd/WIL
WIL - useState 사용해서 To do list 만들기
키싸
2023. 6. 20. 00:19
지난 한주는 React를 배우기 시작했다.
입문강의를 듣고 과제까지 하면서 느낀점은 리액트는 확실히 직관적인 언어구나.
물론 여전히 여럽지만, 직관적으로 문제를 풀어가면서 개발이 점점 더 재밌어진다.
이번주 과제에서는 리액트로 투두리스트를 만들었는데, 과제를 만들면서 to do를 구성하는 initail state를 만들고 이를 mapping, 그리고 filtering 하는 데 어려움이 있었다. 아무래도 충분히 이해하지 못하고 과제를 진행했던 것 같다.
하지만 컴포넌트를 따로 만들어주고 각각의 이벤트핸들러를 생성해서 적용해주어 원하는 기능을 구현했다.
1. 메인 컴포턴트에서는 전체적인 레이아웃과 상태를 관리해줬다.
- 여기서 useState를 사용해 리렌더링 되어야 하는 부분의 각 state를 생성해주고,
- 각 버튼에 해당하는 이벤트 핸들러를 생성해서 카드를 추가, 삭제, 완료, 완료취소 하는 기능을 만들어줬다.
- 각 이벤트 핸들러에서는 filter와 find 메소드를 사용해서 클릭한 값과 다르거나 같은 카드를 찾아줬고
- useState로 리렌더링 해줬다.
function App() {
const [title, setTitle] = useState(""); // 1. task 리렌더링을 위한 상태변수와 상태변경함수, useState함수 세팅
const [context, setContext] = useState("");
const [cards, setCards] = useState([]); // cards 영역, array는 []로 처리
const [doneCards, setDoneCards] = useState([]); // 완료cards 영역
const onSubmitHandler = () => {
setCards([...cards, { title, context }]); // distructuring 후 신규 카드 추가
setTitle(""); // title 필드 초기화
setContext(""); // context 필드 초기화
};
// card.title과 클릭한 카드의 타이틀이 같지않거나
// card.context와 클릭한 카드 내용이 같지 않은 것들을 골라서
// 즉, 전체 카드들을 돌면서 클릭한 카드와 다른 값을 가진 카드들을 리스트업하고
// 그걸 setCards로 리렌더링해준다.
const onDeleteHandler = (cardTitle, cardContext) => {
const updatedCards = cards.filter(
(card) => card.title !== cardTitle || card.context !== cardContext
);
setCards(updatedCards);
};
// 위와 같이 클릭한 카드와 값이 다른 카드들을 골라서 필터링하고 다시 setCards 해주고
// 완료된 카드 목록에는 클릭한 카드와 내용이 같은 카드를 찾아서
// setDoneCard에 기존 값을 distructuring 후 찾은 카드를 추가해서 다시 리스트업, 렌더링 해준다!
const onDoneHandler = (cardTitle, cardContext) => {
const updatedCards = cards.filter(
(card) => card.title !== cardTitle || card.context !== cardContext
);
const doneCard = cards.find(
(card) => card.title === cardTitle && card.context === cardContext
);
setCards(updatedCards);
setDoneCards([...doneCards, doneCard]);
};
const onUnDoneHandler = (cardTitle, cardContext) => {
const updatedDoneCards = doneCards.filter(
(card) => card.title !== cardTitle || card.context !== cardContext
);
const undoneCard = doneCards.find(
(card) => card.title === cardTitle && card.context === cardContext
);
setDoneCards(updatedDoneCards);
setCards([...cards, undoneCard]);
};
const onDeleteHandlerForUnDone = (cardTitle, cardContext) => {
const updatedDoneCards = doneCards.filter(
(card) => card.title !== cardTitle || card.context !== cardContext
);
setDoneCards(updatedDoneCards);
};
return (
<div>
<div>
<header>
<div >My Task List</div>
</header>
<div>
<div>Task</div>
<div>
<textarea
value={title}
type="text"
className="inputTitle"
placeholder=" 제목"
onChange={(event) => setTitle(event.target.value)}
id="myTitlearea"
/>
</div>
<div>
<textarea
value={context}
type="text"
className="inputContext"
placeholder=" 내용"
onChange={(event) => setContext(event.target.value)}
id="myContextarea"
/>
</div>
<button className="mainBtn" onClick={onSubmitHandler}>
추가하기
</button>
</div>
<div>
<div>
<div>WORKING</div>
{cards.map((card, index) => (
<Cards
key={index}
title={card.title}
context={card.context}
onDelete={onDeleteHandler}
onDone={onDoneHandler}
/>
))}
</div>
<div>
<div>DONE</div>
{doneCards.map((card, index) => (
<DoneCards
key={index}
title={card.title}
context={card.context}
onDelete={onDeleteHandlerForUnDone}
onUnDone={onUnDoneHandler}
/>
))}
</div>
</div>
</div>
</div>
);
}
2. 생성된 카드를 보여주는 컴포넌트와 완료된 카드리스트를 보여주는 컴포넌트를 추가로 만들었다.
- 구조분해할당을 사용해 부모 컴포넌트인 App 컴포넌트에서 전달한 속성값을 받아와 내용을 보여줬고
- 삭제와 완료버튼을 만들어 각 버튼에 해당되는 콜백함수를 만들어줬다.
- 여기서 부모 컴포넌트에 있는 onDelHandler, onDoneHandler ~~~ 함수가 작동될 때마다(버튼을 누를 때마다) 그 함수는 onDelete, onDone 콜백함수를 호출하고, 결국 최종적으로는 이 차일드 컴포넌트의 콜백함수를 통해 title, context를 인수(함수를 호출할 때 전달되는 값들)로 부모 컴포넌트의 함수에 전달해준다!
function Cards({ title, context, onDelete, onDone }) { // 구조분해할당
const onDelHandler = () => {
onDelete(title, context);
}; // 콜백함수
const onDoneHandler = () => {
onDone(title, context);
}; // 콜백함수
return (
<div>
<div>{title}</div>
<div>{context}</div>
<div>
<button onClick={onDelHandler}>
삭제하기
</button>
<button onClick={onDoneHandler}>
완료
</button>
</div>
</div>
);
}
// 완료 카드
function DoneCards({ title, context, onDelete, onUnDone }) {
const onDelHandler = () => {
onDelete(title, context);
};
const onUnDoneHandler = () => {
onUnDone(title, context);
};
return (
<div>
<div>{title}</div>
<div>{context}</div>
<div>
<buttononClick={onDelHandler}>
삭제하기
</button>
<button onClick={onUnDoneHandler}>
완료취소
</button>{
</div>
</div>
);
}
근데 이렇게 해서 나는 id 값을 랜덤리 지정해주지 않아도 기능을 구현했다. 그치만 다시 만들어보면서는 initial state 값을 만들고, uuid 라이브러리를 써서 유니크한 id값도 줘봐야겠다.