TIL
리액트로 투두리스트를 구현하면서, JSX문법 사용과 랜더링에 대해 배울 수 있었다.
리랜더링이 끝나는 시점에 대해 알 수 있었고, useState를 사용하여 리액트에게 변경값을
알려주는 개념을 직접 구현해보니 조금은 더 알 수 있는 계기가 되었다.
1.todolist 필수 구현
1. 제목과 내용을 입력하고, [추가하기] 버튼을 클릭하면
Working에 새로운 Todo가 추가되고 제목 input과 내용 input은 다시 빈 값으로 바뀌도록 구성
2. Todo의 isDone 상태가 true이면, 상태 버튼의 라벨을 취소, isDone이 false 이면 라벨을 완료 로 조건부 렌더링
3. Todo의 상태가 Working 이면 위쪽에 위치하고, Done이면 아래쪽에 위치하도록 구현
4. Layout의 최대 넓이는 1200px, 최소 넓이는 800px로 제한하고, 전체 화면의 가운데로 정렬
5. 컴포넌트 구조는 자유롭게 구현 > 분리한 컴포넌트를 README에 작성
[1]
제목과 내용을 입력하고, [추가하기] 버튼을 클릭하면
Working에 새로운 Todo가 추가되고 제목 input과 내용 input은 빈 값으로 바뀌도록 구성
(★ point) 변수 initialState의 id는 꼭 '1'로 시작해야함
-> 해당 배열의 길이+1이 다음 id값으로 사용하기 위함
const initialState = [{ id: 1, title: '해야할 일', content: '리액트 개인과제', isDone: false }];
const [toDo, setTodo] = useState(initialState);
const titleHandler = (event) => {
let titleData = event.target.value;
setTitle(titleData);
};
const contentHandler = (event) => {
let contentData = event.target.value;
setContent(contentData);
};
<div className="inputList">
제목 <input value={title} onChange={titleHandler}></input>
내용 <input value={content} onChange={contentHandler}></input>
<Button className="btnAdd" onClick={BtnAddClick}>
추가하기
</Button>
</div>
//추가하기 버튼
const BtnAddClick = () => {
//1. 새로운 형태의 todolist (initialState:객체) 생성
// todolist: { id: 0, title: '', content: '', isDone: false }
//2. todolist를 배열에 더한다.
const newTodo = {
id: toDo.length + 1,
title: title,
content: content,
isDone: false,
};
//불변성을 유지하기 위해서 스프레드 문법을 사용
//React에게 state가 변경됨을 알려줌
setTodo([...toDo, newTodo]); //비동기
setTitle(''); // 아이디 검색 구간 초기화
setContent(''); //내용 검색 구간 초기화
};
[2]
Todo의 isDone 상태가 true이면, 상태 버튼의 라벨을 취소,
isDone이 false 이면 라벨을 완료 로 조건부 렌더링
(위치: src / components / Newtodo.jsx)
<button className="changeBtn" onClick={(event) => BtnStateChangeClick(item.id)}>
{item.isDone ? '취소' : '완료'}
</button>
[3]
Todo의 상태가 Working 이면 위쪽에 위치하고, Done이면 아래쪽에 위치하도록 구현
(★ point) filter를 사용해서 Working 이면 위쪽(isDone === false) Done이면 아래쪽 (isDone === true)로 배치.
<div className="working-style">
<h2>✍️Working</h2>
<div className="working-todo">
{' '}
{toDo
.filter((item) => item.isDone === false)
.map(function (item) {
return (
<Newtodo
key={item.id}
item={item}
BtnDeleteClick={BtnDeleteClick}
BtnStateChangeClick={BtnStateChangeClick}
/>
);
})}
</div>
</div>
<div className="working-style">
<h2>✌️Done✌️</h2>
<div className="working-todo">
{toDo
.filter((item) => item.isDone === true)
.map(function (item) {
return (
<Newtodo
key={item.id}
item={item}
BtnDeleteClick={BtnDeleteClick}
BtnStateChangeClick={BtnStateChangeClick}
/>
);
})}
</div>
[4]
Layout의 최대 넓이는 1200px, 최소 넓이는 800px로 제한하고, 전체 화면의 가운데로 정렬
(위치: src / App.css)
.layout {
display: flex;
justify-content: center;
flex-direction: column;
margin: auto;
max-width: 1200px;
min-width: 800px;
}
[5]
컴포넌트 구조는 자유롭게 구현 -- 4가지 생성
(위치: src/ components/Newtodo.jsx & InputList.jsx & Contents.jsx & Button.jsx)
import { useState } from 'react';
import './App.css';
import InputList from './components/InputList'; //1. 제목+ 내용 : 추가하는 영역
import Contents from './components/Contents'; //2. 해야할 부분 / 할일 영역 나뉜 부분
import Newtodo from './components/Newtodo'; //2-1. (삭제버튼 / 완료-취소 버튼 / 저장된 할일 제목+내용)
function App() {
const initialState = [{ id: 1, title: '해야할 일', content: '리액트 개인과제', isDone: false }];
const [toDo, setTodo] = useState(initialState);
return (
<div className="layout">
<div className="top">
<h3>My Todo List📝</h3>
<h3>React</h3>
</div>
<InputList toDo={toDo} setTodo={setTodo} />
<Contents toDo={toDo} setTodo={setTodo} />
</div>
);
}
export default App;
[완성본]