const [toDos, setToDos] = useState([
{ id: 1, title: "장보기", content: "고기100g, 우유1팩", isDone: true },
{ id: 2, title: "고양이", content: "사료 챙겨주기, 털 빗질", isDone },
{ id: 3, title: "리액트 공부", content: "과제 시도해보기", isDone },
]);
const registerBtnHndlr = function () {
const newTodo = { id: toDos.length + 1, title, content, isDone };
setToDos([...toDos, newTodo]);
console.log(toDos);
};
일단 진행중인 toDo가 표시되는 영역과 완료된 toDo가 표시되는 영역 모두에 똑같이 toDos 배열의 모든 객체를 전부 표시하고 isDone 값에 따라서 해당 div의 className을 변경,
변경된 className을 통해 CSS에서 display:none
이나 display:block
을 설정하는 것을 시도해 보기로 한다.
일단 진행중/완료 양쪽 영역 모두에 정상적이게 toDo 들이 표시되게 하는 것을 시도.
const doneCancelBtnHndlr = (id) => {
setToDos((prevToDos) => {
return prevToDos.map((item) => {
if (item.id === id) {
return { ...item, isDone: !item.isDone };
}
return item;
});
});
};
위의 코드로 isDone 값을 독립적으로 바꾸는데까지 성공했다. 또한 isDone 이 true인 경우 버튼에 취소
라고 표시되고 false인 경우에는 완료
라고 표시되게 하는데까지 성공. {item.isDone ? "취소" : "완료"}
이 코드로 생각보다 간단하게 구현했다.
js 에서는 함수를 만들고 그 함수를 버튼 클릭에 할당후 함수 내에서 classList.add
나 setAttribute
로 버튼 클릭에 따라 class 명을 어렵지 않게 바꿀수 있었다. 리액트에서는 어떻게 해야 할까?
const divClassName = isDone ? "complete" : "not-complete";
const combinedClassName = `todo-block ${divClassName}`;
combinedClassName을 한번 거쳐서 toDo 블록들 전체의 모양을 제어하는데 쓸 className도 함께 병기되도록 시도해 보았다.
시도 1 결론
완전히 실패. isDone은 버튼 클릭에 따라 바로바로 바뀌지만 해당 div 의 className 은 처음 만들어질때의 값 그대로다. 심지어 분명 isDone 이 true 라서 "complete"로 표시되어야할 div 조차 "not-complete"로 표시되는 상황 발생
시도 1 피드백
왜 "complete"로 표시되어야할 div 조차 "not-complete"로 표시되는지에 대한 해답을 알았다. isDone은 state로 설정되어 있는데, 초기값이 true로 되어 있어서 항상 divClassName 이 "complete"로 설정 되는 것이다.
게다가 생각해보니, isDone이 state인데 setIsDone으로 isDone을 변경하면 isDone이 사용된 모든 toDo객체가 한번에 변경된다는 문제점도 있다는 것을 발견했다. 구글링 해보니 이에 관한 내용이 한가득 있다.
현재까지 파악한 바로는 모든 state를 전역 state로 선언해야 하는 것은 아니며 const let 을 함수 내에서 선언해서 그 함수 내에서만 쓰는 것과 마찬가지로 state도 특정 컴포넌트 내에서만 사용되는 local state로 만들수 있다고 한다. isDone을 지역상태로 사용하는 방법을 고민해 봐야하는 시점이다.