TIL | #14 React | TodoList 만들기 #1
2021-04-05(월)
리액트 생성 및 모듈 설치
yarn react create-app todoList
node-sass, classnames, react-icons 설치
yarn add node-sass classnames react-icons
- TodoTemplate
앱 타이틀을 보여준다. children으로 내부 jsx를 props 받아서 렌더링 - TodoInsert
새로운 항목 입력 및 추가 state를 통해 인풋 상태 관리 - TodoListItem
항목에 대한 정보를 보여준다. 객체를 props로 받아와서 상태에 따라 다른 스타일을 보여준다. - TodoList
할 일 배열을 props로 받아 온 후 map을 이용, 여러개의 TodoListItem 컴포넌트 변환
// App.js
import React, { useCallback, useRef, useState } from 'react';
import TodoTemplate from './components/TodoTemplate';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
const App = () => {
const [todos, setTodos] = useState([
{
id: 1,
text: '삼선짬뽕 먹기',
checked: true,
},
{
id: 2,
text: '바느질 하기',
checked: true,
},
{
id: 3,
text: '샤워하기',
checked: false,
},
]);
// useRef로 컴포넌트에서 사용할 변수를 만드는 이유는
// id값은 렌더링되는 정보가 아니기 때문이다.
// 즉 화면에 보이지도 않고 이 값이 바뀐다고해서
// 컴포넌트가 다시 렌더링 될 이유가 없기 때문이다.
// 단순히 새로운 항목을 만들때 참조되는 값이다.
const nextId = useRef(4);
const onInsert = useCallback(
(text) => {
const todo = {
id: nextId.current,
text,
checked: false,
};
setTodos(todos.concat(todo));
nextId.current += 1;
},
[todos],
);
const onDelete = useCallback(
(id) => {
const nextTodos = todos.filter((todo) => todo.id !== id);
setTodos(nextTodos);
},
[todos],
);
const onChecked = useCallback(
(id) => {
const nextTodos = todos.filter((todo) => {
if (todo.id === id) todo.checked = !todo.checked;
return todo;
});
setTodos(nextTodos);
},
[todos],
);
return (
<TodoTemplate>
<TodoInsert onInsert={onInsert} />
{/* TodoList props로 전달 -> TodoList에서 이 값을 받아온 후
TodoListItem으로 변환하여 렌더링 하도록 설정한다.*/}
<TodoList todos={todos} onDelete={onDelete} onChecked={onChecked} />
</TodoTemplate>
);
};
export default App;
TodoTemplate
// TodoTemplate.js
import React from 'react';
import './TodoTemplate.scss';
const TodoTemplate = ({ children }) => {
return (
<div className="TodoTemplate">
<div className="app-title">일정 관리</div>
<div className="content">{children}</div>
</div>
);
};
export default TodoTemplate;
// TodoTemplate.scss
.TodoTemplate {
width: 512px;
margin-left: auto;
margin-right: auto;
margin-top: 6rem;
border-radius: 4px;
overflow: hidden;
}
.app-title {
background: #22b8cf;
color: white;
height: 4rem;
font-size: 1.5rem;
display: flex;
align-items: center;
justify-content: center;
}
.content {
background: white;
}
TodoInsert
// TodoInsert.js
import React, { useCallback, useState } from 'react';
import { MdAdd } from 'react-icons/md';
import './TodoInsert.scss';
const TodoInsert = ({ onInsert }) => {
const [value, setValue] = useState('');
const onChange = useCallback((e) => {
setValue(e.target.value);
}, []);
const onSubmit = useCallback(
(e) => {
onInsert(value); //현재 useState를 통해 관리하고 있는 value값을 인자로 전달
setValue(''); //value값 초기화
e.preventDefault();
},
[onInsert, value],
);
return (
<form className="TodoInsert" onSubmit={onSubmit}>
<input placeholder="할일 입력" value={value} onChange={onChange} />
<button type="submit">
<MdAdd />
</button>
</form>
);
};
export default TodoInsert;
.TodoInsert {
display: flex;
background: #495057;
input {
// 기본 스타일 초기화
background: none;
outline: none;
border: none;
padding: 0.5rem;
font-size: 1.2rem;
line-height: 1.5;
color: white;
&::placeholder {
color: #dee2e6;
}
// 버튼을 제외한 영역 모두 차지
flex: 1;
}
button {
// 기본 스타일 초기화
background: none;
outline: none;
border: none;
background: #868e96;
color: white;
padding-left: 1rem;
padding-right: 1rem;
font-size: 1.5rem;
display: flex;
align-items: center;
cursor: pointer;
transition: 0.1s background ease-in;
&:hover {
background: #adb5db;
}
}
}
TodoList
// TodoList.js
import React from 'react';
import TodoListItem from './TodoListItem';
import './TodoList.scss';
const TodoList = ({ todos, onDelete, onChecked }) => {
return (
<div className="TodoList">
{todos.map((todo) => (
<TodoListItem
todo={todo}
key={todo.id}
onDelete={onDelete}
onChecked={onChecked}
/>
))}
</div>
);
};
export default TodoList;
// TodoList.scss
.TodoInsert {
display: flex;
background: #495057;
input {
// 기본 스타일 초기화
background: none;
outline: none;
border: none;
padding: 0.5rem;
font-size: 1.2rem;
line-height: 1.5;
color: white;
&::placeholder {
color: #dee2e6;
}
// 버튼을 제외한 영역 모두 차지
flex: 1;
}
button {
// 기본 스타일 초기화
background: none;
outline: none;
border: none;
background: #868e96;
color: white;
padding-left: 1rem;
padding-right: 1rem;
font-size: 1.5rem;
display: flex;
align-items: center;
cursor: pointer;
transition: 0.1s background ease-in;
&:hover {
background: #adb5db;
}
}
}
TodoListItem.js
import React, { useCallback } from 'react';
import cn from 'classnames';
import './TodoListItem.scss';
import {
MdCheckBoxOutlineBlank,
MdCheckBox,
MdRemoveCircleOutline,
} from 'react-icons/md';
const TodoListItem = ({ todo, onDelete, onChecked }) => {
const { id, text, checked } = todo;
const remove = useCallback(
(e) => {
onDelete(id);
},
[onDelete, id],
);
const check = useCallback(
(e) => {
onChecked(id);
},
[onChecked, checked],
);
return (
<div className="TodoListItem">
<div className={cn('checkbox', { checked })} onClick={check}>
{checked ? <MdCheckBox /> : <MdCheckBoxOutlineBlank />}
<div className="text">{text}</div>
</div>
<div className="remove" onClick={remove}>
<MdRemoveCircleOutline />
</div>
</div>
);
};
export default TodoListItem;
// TodoListItem.scss
.TodoListItem {
padding: 1rem;
display: flex;
align-items: center;
&:nth-child(even) {
background: #f8f9fa;
}
}
.checkbox {
cursor: pointer;
flex: 1;
display: flex;
align-items: center;
svg {
font-size: 1.5rem;
}
.text {
margin-left: 0.5rem;
flex: 1;
}
&.checked {
svg {
color: #22b8cf;
}
.text {
color: #adb5bd;
text-decoration: line-through;
}
}
}
.remove {
display: flex;
align-items: center;
font-size: 1.5rem;
color: #ff6b6b;
&:hover {
color: #ff8787;
}
& + & {
border-top: 1px solid #dee2e6;
}
}
Author And Source
이 문제에 관하여(TIL | #14 React | TodoList 만들기 #1), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@goblin820/React-TodoList-만들기-1저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)