1. 개인과제
- Goal : My Todo List 만들기
- features :
- UI 구현하기
- Todo 추가 하기
- Todo 삭제 하기
- Todo 완료 상태 변경하기 (완료 ↔ 진행중)
- Requirement :
- 제목과 내용을 입력하고, [추가하기] 버튼을 클릭하면 Working에 새로운 Todo가 추가되고 제목 input과 내용 input은 다시 빈 값으로 바뀌도록 구성해주세요.
- Todo의 isDone 상태가 true이면, 상태 버튼의 라벨을 취소, isDone이 false 이면 라벨을 완료 로 조건부 렌더링 해주세요. 위 영상을 보면 버튼 내 라벨이 다른 걸 볼 수 있죠?
- Todo의 상태가 Working 이면 위쪽에 위치하고, Done이면 아래쪽에 위치하도록 구현합니다.
- Layout의 최대 넓이는 1200px, 최소 넓이는 800px로 제한하고, 전체 화면의 가운데로 정렬해주세요.
- 컴포넌트 구조는 자유롭게 구현해보세요.
나의 풀이
// React를 사요한다. state hook을 사용하기 위해 { useState } 를 불러옴
import React, { useState } from "react";
import "./App.css"; // 🔥 반드시 App.css 파일을 import 해줘야 합니다.
// id 값의 혼선을 방지하기 위해 key값으로 uuid를 설정하였음.
// 자세한 내용은 본문의 uuid를 참고
import uuid from "react-uuid";
const App = () => {
// todoLists 객체로 state hook을 지정해주었음
const [todoLists, setTodoLists] = useState([
{
// 순서를 지정하기 위해 id값을 지정하였음
id: 0,
// id값만 가지고 있을 경우 지칭상의 혼선이 올수 있기 때문에 uuid를 지정하였음
key: uuid(),
title: "리액트 공부하기",
desc: "리액트 기초를 공부해봅시다",
// 진행중과 완료 상태를 표현하기 위해 isDone 값을 true, false로 지정하였음
isDone: false,
},
{
id: 1,
key: uuid(),
title: "리액트 알아보기",
desc: "리액트 기초를 알아봅시다",
isDone: true,
},
]);
const deleteTodoListHandler = (key) => {
// '삭제' 버튼을 클릭했을때 실행되는 handler
// filter를 통해서 받아오는 key값과 다른 key값을 가진 객체들만으로 새로운 배열을 구성해서 state로 넘겨주는 것.
const newTodoList = todoLists.filter((todoList) => todoList.key !== key);
setTodoLists(newTodoList);
};
const onChangeHandler = (key) => {
// 완료 혹은 취소 버튼을 클릭했을때 실행되는 handler
// 받아오는 key값과 같은 key값을 가진 객체를 배열에서 찾아 그 인덱스 값을 리턴하는 함수 findIndex를 선언
const index = todoLists.findIndex((todoList) => todoList.key === key);
// spread order로 todoLists 배열의 복사본을 생성하였음
// ???? 복사본은 왜 만들어야 할까? 원본은 어떻게 되는 걸까?
// !!!! 기존에 있던 배열의 속성을 변경해주면 불변성문제 때문에 react에서 속성이 변한것을 감지하지 못하고 렌더링을 하지 않는다.
// !!!! 그렇기 때문에 copyTodo를 생성해줘서 주소값을 변경해주어야 한다.
const copyTodo = [...todoLists];
// 이 부분이 해석이 안된다.
// ???? ...copyTodo[index] 이것은 무슨 의미일까?
// !!!! ...copyTodo[index] 이것은 copyTodo[index] 내의 모든 속성을 복사해준다는 의미이다.
// ???? spread syntax의 정확한 의미는 무엇일까?
// !!!! spread syntax는 배열 값을 복사해온다.
if (copyTodo[index].isDone === true) {
copyTodo[index] = { ...copyTodo[index], isDone: false };
} else {
copyTodo[index] = { ...copyTodo[index], isDone: true };
};
// setTodoLists 로 새로운 배열을 복사해준다.
setTodoLists(copyTodo);
};
const workingLists = todoLists
// 객체중에서 isDone 값이 false인 값을 골라 새로운 배열을 만들어줌
.filter((todoList) => todoList.isDone === false)
// 각각의 요소에 대해 한번씩 순서대로 불러 그 함수의 반환값으로 새로운 배열을 만들어줌
.map((todoList) => {
return (
// child component인 List를 불러주고 아래와 같이 값을 넣어준다.
<List
handleDelete={deleteTodoListHandler}
handleDone={onChangeHandler}
todoList={todoList}
key={todoList.key}
></List>
);
});
const doneLists = todoLists
.filter((todoList) => todoList.isDone === true)
.map((todoList) => {
return (
<List
handleDelete={deleteTodoListHandler}
handleDone={onChangeHandler}
todoList={todoList}
key={todoList.key}
></List>
);
});
// 새로운 title과 desc를 받아 새로운 객체를 만들기 위해 state hook을 선언해줌
const [title, setTitle] = useState("");
const [desc, setDesc] = useState("");
// 추가하기를 클랙했을때 실행할 event handler
const onSubmitHandler = () => {
// title 과 desc가 아무것도 입력되지 않으면 실행되지 않도록 if 함수를 사용함.
if (title !== "" && desc !== "") {
// 새로운 객체를 생성해줌. isDone의 기본값은 false이다.
const newTodoLists = {
id: todoLists.length + 1,
key: uuid(),
title: title,
desc: desc,
isDone: false,
};
// todoLists 배열을 복사해주고 newTodoLists를 추가해줌
setTodoLists([...todoLists, newTodoLists]);
} else {
return null;
}
};
return (
<div class="layout">
<div className="container">
<div>My Todo List</div>
<div>React</div>
</div>
<div className="add-form">
<div className="input-style">
<label className="form-label">제목</label>
<input
className="add-input"
// 입력되는 값을 title로 넘겨줌
value={title}
// state함수를 이용하여 입력되는 값을 배열로 넘겨줌
onChange={(e) => setTitle(e.target.value)}
/>
<label className="form-label">내용</label>
<input
className="add-input"
value={desc}
onChange={(e) => setDesc(e.target.value)}
/>
</div>
<button className="add-button" onClick={onSubmitHandler}>
추가하기
</button>
</div>
<div className="list-container">
<div>
<h2 className="list-title">Working.. 🔥</h2>
<div
className="app-style" // 렌더링되도록 {workingLists} 변수를 받아옴
>
{workingLists}
</div>
</div>
<div>
<h2 className="list-title">Done..! 🎉</h2>
<div className="app-style">{doneLists}</div>
</div>
</div>
</div>
);
};
// child component인 List를 설정
function List(props) {
return (
// 컴포넌트로 이러한 사각형 형태의 카드를 만들어준다.
<div className="square-style">
<h2 // props를 이용해 부모 conponent에서 title을 받아와서 이부분에 렌더링한다.
>
{props.todoList.title}
</h2>
<div>{props.todoList.desc}</div>
<div className="button-set">
<button
className="todo-delete-button button"
// props로 handleDelete값을 받아오면서 key를 함수로 넘겨준다.
onClick={() => {
props.handleDelete(props.todoList.key);
}}
>
삭제
</button>
<button
className="todo-complete-button button"
onClick={() => {
props.handleDone(props.todoList.key);
}}
// isDone 값에 따라 버튼의 글자가 바뀌도록 삼항연산자를 설정하고 렌더링해준다.
// {조건문 ? true인 경우 : false인 경우}
>
{props.todoList.isDone === true ? "취소" : "완료"}
</button>
</div>
</div>
);
}
export default App;
주요한 기능 구현
1. deleteTodoListHandler
- 개요
각 객체를 식별해주는 key 값을 비교하여 해당되지 않는 객체만으로 새로운 배열을 복사하는 식으로 구현하였다.
- point
- 객체를 식별하는 문제:
- id값이 아닌 key값을 새로 설정하여서 혼선이 없도록하였다. "삭제하기"를 클릭하는 경우에는 key값이 함수로 전달되도록 'props.todoList.key' 를 통해 props로 받아온 key를 전달해준다. - filter의 사용 :
- filter는 callback 함수 조건에 맞는 배열 요소로 새로운 배열을 반환해준다.
todoLists.filter((todoList) => todoList.key !== key)
- 위의 구문을 해석하자면 todoLists라는 배열의 filter메소드를 적용한다. 배열 내 요소를 todoList로 설정해주고 todoList의 key 값(todoList.key)이 함수에서 받아온 key값과 같지 않은 요소들로 새로운 배열을 만들어준다.
2. onChangeHandler
- 개요
객체를 식별해주는 key값을 확인하고 그 값을 가지고 있는 객체의 속성 중 isDone 값을 변경하도록 만들어야한다.
- point
- key값을 가진 객체를 식별하는 문제
- key값을 가지고 있는 배열 요소의 index를 구한다.
findIndex를 사용하여 key값을 가지고 있는 요소의 index를 구할수 있다. - copyTodo[index]와 같이 값을 입력해줌으로써 배열 요소에 접근할 수 있다.
- key값을 가지고 있는 배열 요소의 index를 구한다.
- 기존의 있는 배열이 아닌 함수에서 내려주는 key값을 가진 객체의 속성 중 isDone값을 변경해주는 문제
const copyTodo = [...todoLists];
- 기존에 있던 배열의 속성을 변경해주면 불변성문제 때문에 react에서 속성이 변한것을 감지하지 못하고 렌더링을 하지 않는다.
- 그렇기때문에 copyTodo를 생성해줌으로써 주소값을 변경해주는 것이다.
- if 함수를 사용하여 copyTodo[index].isDone의 값이 true 혹은 false인 경우에 따라 변경되는 값이 다르도록 설정하였다.
copyTodo[index] = { ...copyTodo[index], isDone: false };
- copyTodo[index]을 새로 정의해준다.
- { ...copyTodo[index], isDone: false }
copyTodo[index]의 모든 속성을 복사해주되, isDone의 값은 false로 변경해준다.
setTodoLists(copyTodo);
- state를 통해 새로 정의된 배열을 update해준다.
3. id가 아닌 객체를 식별해주는 uuid 설정
- length로 id값을 주고 식별하는 경우에 순서가 같으면 식별되지 않는 문제가 발생한다.
- 그렇기 때문에 length가 아닌 다른 고유값을 부여하는 식별자가 필요하다.
- 국제기구에서 표준으로 정한 범용고유식별자(UUID)를 사용하였다.
- 16 옥텟(128비트)의 수로 32개의 16진수로 표현 - 36개의 문자로 이루어져있으며 중복가능성이 거의 없다.
- 사용법은 블로그를 통해 확인
리액트 id에 고유값 주기
아이디에 고유 값 주기
이 주제로 블로깅을 하게 된 이유
velog.io
추가 공부
전개구문은 무엇인가?
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Spread_syntax
전개 구문 - JavaScript | MDN
전개 구문을 사용하면 배열이나 문자열과 같이 반복 가능한 문자를 0개 이상의 인수 (함수로 호출할 경우) 또는 요소 (배열 리터럴의 경우)로 확장하여, 0개 이상의 키-값의 쌍으로 객체로 확장시
developer.mozilla.org
삼항연산자 사용방법
react 조건부 연산자_삼항연산자
JSX 내부의 자바스크립트 표현식에서 if 문을 사용할 수는 없다.하지만, 조건에 따라 다른 내용을 렌더링해야 할때는JSX 밖에서 if 문을 사용하여 사전에 값을 설정한다.{ } 안에서 조건부 연산자\_
velog.io
객체 배열에서 값변경해주기
https://developer-talk.tistory.com/246
[React]객체 배열에서 값 변경
이번 포스팅에서는 React에서 객체의 배열을 가지는 state 값을 변경하는 방법을 소개합니다. 다음과 같이 객체의 배열(objArray)을 가지는 state가 있다고 가정합니다. state = { objArray: [ { code: "", name: ""
developer-talk.tistory.com
'Hanghae99' 카테고리의 다른 글
| 221129 TIL 07 문서 객체 모델 1 (0) | 2022.12.01 |
|---|---|
| 221130 TIL 팀별 과제 정리 (0) | 2022.12.01 |
| 221128 TIL React 입문주차 3 (0) | 2022.11.29 |
| 221126 TIL React 입문 주차 2 (0) | 2022.11.28 |
| 221125 TIL React 입문 주차 1 (0) | 2022.11.25 |