React 사용 목적에 맞게 Component를 나누어서 작성
HTML input 태그의 onChange 이벤트를 이용해 값을 받는다
Component의 State 값으로 String 변수를 이용
const [inputTodoValue, setInputTodoValue] = useState("");
onChange 이벤트가 발생할 때 마다 setState를 하여 값을 저장하고 있는다
State 값은 input의 값이 된다
const onChangeInputList = (event) => {
const { value } = event.target;
setInputTodoValue(value);
};
// ...
return // ...
<input
className="ToDo-Add-Input"
onChange={onChangeInputList}
value={inputTodoValue}
/>
// ...
입력을 했다면 할 일을 추가할 수 있어야 한다
HTML Button 태그의 onClick 이벤트가 발생하면 일을 추가한다
Component의 State 값으로 id를 가지고 있어 일을 추가할 때 마다 고유한 id를 부여한다
const [id, setId] = useState(0);
onClick 이벤트 발생 시 입력한 값(inputTodoValue)가 있을 때 할 일을 추가(상위 Component의 setState) 한다
list에 현재 id 값을 부여하고 id + 1을 setState 하여 각 할 일이 고유한 id를 가지고 있게 한다
const onClickAddList = () => {
if (inputTodoValue) {
setList((prev) => [
...prev,
{
id: id,
content: inputTodoValue,
isComplete: false,
isUpdating: false
}
]);
setId((prev) => prev + 1);
setInputTodoValue("");
}
};
return // ...
<button
className="ToDo-Add-Button"
onClick={onClickAddList}>
ADD
</button>
// ...
배열로 저장하고 있는 객체들을 map() 함수를 이용해 HTML li 태그로 출력한다
하위 컴포넌트에서 출력을 담당하도록 하게하고 나열만 하여 기능을 명확히 했다
// ...
function ToDoList({ list, setList }) {
// ...
return (<ul className="ToDo-List">
{list.map((value) => (
<ToDoItem
key={value.id}
id={value.id}
content={value.content}
isComplete={value.isComplete}
isUpdating={value.isUpdating}
pressEnterKey={pressEnterKey}
/>
))}
</ul>);
}
각각의 li 태그가 같은 onClick 이벤트를 실행할 예정이므로 이벤트 버블링을 활용한다
→ HTML ul 태그에 onClick 이벤트 하나를 두고 Event Target의 id를 통해 기능을 수행하도록 한다
id를 ToDo-[기능]-[번호] 로 부여하여 어떤 기능을 수행해야 하는지 알아내고 어느 타겟에 적용해야 하는지 알아낸다
const onClickList = (event) => {
if (event.target.id && event.target.id.split('-').length > 1) {
const buttonFunction = event.target.id.split('-')[1]
// ... 기능 수행
}
list 객체의 속성 중 isComplete가 true 면 완료, false면 할 일이 남은 것이다
default 값은 false로 두었다
isComplete의 값에 따라 Button의 Display를 달리하였다
return // ...
<button
className={isComplete ? "ToDo-Item-Done" : "ToDo-Item-Left"}
id={"ToDo-Complete-" + String(id)}
>
{isComplete ? "✓" : "-"}
</button>
// ...
클릭 된 Button은 해당 객체의 isComplete 값만 바꾸게 된다
if(buttonFunction === "Complete"){
setList(
list.map((value) => {
if (event.target.id === "ToDo-Complete-" + String(value.id)) {
return { ...value, isComplete: !value.isComplete };
}
return value;
})
);
}
삭제 Button을 누르면 List 배열 중에서 해당 객체만 삭제하면 된다
많은 방법이 있지만 filter() 함수를 사용했다
if(buttonFunction === "Delete"){
setList(
list.filter((value) => event.target.id !== "ToDo-Delete-" + String(value.id))
);
}
객체는 isUpdating 이라는 속성을 가지고 있어 수정하고 있는지에 대한 값을 저장하고 있다
default 값은 false이고 클릭하면 isUpdating의 값을 true로 바꿀 뿐이다
if(buttonFunction === "Update"){
setList(
list.map((value) => {
if (event.target.id === "ToDo-Update-" + String(value.id)) {
return { ...value, isUpdating: !value.isUpdating };
}
return value;
})
);
}
isUpdating의 값에 따라 li 태그의 출력이 달라진다
return // ...
{isUpdating
? <input
className="ToDo-Item-Update"
id={"ToDo-Updating-" + String(id)}
onKeyPress={pressEnterKey}
defaultValue={content}
/>
: <p
className="ToDo-Item-Content"
id={"ToDo-Update-" + String(id)}
>
{content}
</p>
}
// ...
수정이 끝났다면 사용자는 Enter를 눌러 저장할 수 있다
onKeyPress 이벤트에서 13번(Enter)가 발생하면 Input 값을 저장하고 isUpdating을 false로 만든다
const pressEnterKey = (event) => {
if(event.charCode === 13){
setList(
list.map((value) => {
if (event.target.id === "ToDo-Updating-" + String(value.id)) {
return {
...value,
content: event.target.value,
isUpdating: false
};
}
return value;
})
);
}
}
list의 배열 안 객체 중에 isComplete가 false인 객체가 몇 개 인지만 확인하면 된다
const renderComplete = () => {
return list.reduce((accumulator, current) => {
if (current.isComplete === false)
return accumulator + 1;
return accumulator;
}, 0);
}
이 함수에서 반환된 값을 출력하기만 하면 된다
return // ...
<div className="List-Count">할 일 {renderComplete()}개</div>
// ...
);
Date 객체를 이용해서 오늘의 날짜를 출력하도록 했다.
요일은 미리 배열을 선언하고 index에 따라 출력 값을 정하도록 하였다.
const renderDate = () => {
if(nowDate){
const dayToKorean = ['월', '화', '수', '목', '금', '토', '일'];
const year = nowDate.getFullYear();
const month = String(Number(nowDate.getMonth()) + 1);
const date = nowDate.getDate();
const day = nowDate.getDay();
return year + '년 ' + month + '월 ' + date + '일 ' + dayToKorean[day] + '요일';
}
return '';
};
이 또한 반환된 값을 출력하기만 하면 된다
return // ...
<div className="Header">
<h1 className="Header-Title">TODOLIST</h1>
<div className="Header-Day">{renderDate()}</div>
</div>
// ...
);
작년에도 React로 Todo List 만들기를 두 번 정도 했었다
그때는 아무 것도 모르고 기능을 구현하는 것에만 초점에 두고 진행했었는데 역시 기초 공부를 한 뒤에 만드는 것이 얻어가는 것도 많은 것 같다
또한 똑같은 걸 만들더라도 여러번 하면 코딩 속도가 빨라지는 것 같다
디자인 실력은 어떻게 해도 늘지 않는 것 같다 ㅎ...