React를 배우기 전 HTML
, JS
를 복습하기 위해 Todo App을 만들었다.
<body>
<h1 class="title">Todo App</h1>
<div class="todos">
<input class="toggle-all" type="checkbox" />
<input class="todo-input" type="text" placeholder="Write todo..." />
<ul class="todo-list">
<!-- <li data-id="1" class="todo-item">
<input class="toggle" type="checkbox" />
<span class="content">html</span>
<input class="content-edit" type="text" />
<button class="remove" type="button">x</button>
</li> -->
</ul>
<span class="count">1 item left</span>
</div>
</body>
주석으로 처리한 <li> ~ </li>
는 자바스크립트로 동적으로 생성하여 주었다.
todos = todo[]
todo: {
id: number,
content: string,
completed: boolean
}
todos = [
{ id: 0, content: 'HTML', completed: false },
{ id: 1, content: 'CSS', completed: true },
{ id: 2, content: 'JS', completed: false },
];
todo 객체의 배열 todos로 어플리케이션을 컨트롤
todo는 id, 내용, 완료 여부로 구성된 객체
app.js
파일 안에 todos
배열을 만들어 관리하였다.// render.js
const render = () => {
$todoList.innerHTML = todos
.map(
({ id, content, compelted }) => `
<li data-id="${id}" class="todo-item">
<input class="toggle" type="checkbox" ${compelted ? 'checked' : ''} />
<span class="content">${content}</span>
<input class="content-edit hidden" type="text" />
<button class="remove" type="button">x</button>
</li>
`
)
.join('');
$count.textContent = `${todos.length} item${
todos.length > 1 ? 's' : ''
} left`;
};
input.content-edit
은 처음 렌더링 될 때는 숨겼다가 유저가 더블 클릭을 하면 나타나게 구현하였다.span.content
는 숨겨진다.render
함수는 todos
가 변경될 때마다 호출된다.setTodo
를 호출하였고 setTodo
는 render
를 호출한다.const setTodo = _todos => {
todos = [..._todos];
render();
};
todos
가 업데이트 된다.const getNextId = () => Math.max(...todos.map(todo => todo.id), 0) + 1;
const addTodo = content => {
const _todos = [{ id: getNextId(), content, compelted: false }, ...todos];
setTodo(_todos);
};
$todoInput.onkeyup = e => {
const { code } = e;
const todo = e.target.value.trim();
if (code !== 'Enter' || todo === '') return;
e.target.value = '';
addTodo(todo);
};
(todos의 가장 큰 아이디 || 0) + 1
로 생성하였다.addTodo
를 호출하지 않는다).addTodo
는 todos
가장 앞에 새로운 todo
를 삽입한 새로운 _todos
로 setTodo
를 호출한다.input
창이 나온다.$todoList.ondblclick = ({ target }) => {
if (!target.matches('.content')) return;
target.classList.add('hidden');
const contentEdit = target
.closest('.todo-item')
.querySelector('.content-edit');
contentEdit.value = target.textContent;
contentEdit.classList.remove('hidden');
};
const editTodo = (id, content) => {
const _todos = todos.map(todo =>
todo.id === +id ? { ...todo, content } : todo
);
setTodo(_todos);
};
_todos
는 파라미터로 전달 받은 id와 같은 todo만 content를 변경하고 나머지 todo는 그대로 반환된 배열이다.const toggle = id => {
const _todos = todos.map(todo =>
todo.id === +id ? { ...todo, compelted: !todo.compelted } : todo
);
setTodo(_todos);
};
$todoList.onclick = ({ target }) => {
if (!target.matches('.toggle, .remove')) return;
const { id } = target.closest('.todo-item').dataset;
target.matches('.toggle') ? toggle(id) : removeTodo(id);
};
data-id
속성에서 가져왔기 때문에 숫자 타입으로 형 변환을 해주었다.const removeTodo = id => {
const _todos = todos.filter(todo => todo.id !== +id);
setTodo(_todos);
};
_todos
를 setTodo
에 전달한다