리액트 TodoList 만들기
1. 폴더 구성
- App.jsx 파일에 components 폴더에 있는 파일들을 import 해서 사용했다.
- db 폴더에 있는 todo.json 은 첫 화면 랜더링시 나오는 데이터 파일이다.
- 원래 구성은 App.jsx에 모든 파일을 import 하려고 했으나 TodoBoard.jsx 파일에 TodoInput, TodoItem 파일을 import 하게 되었다.
2. index.jsx
- 처음에 있는 구성 그대로이다. 아직 이 부분에 대해서 제대로 이해하지 못해서 건드리지 않았다. 실제로 코드를 짤 때도 index.jsx 파일은 가끔씩 건드린다고 한다.
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
reportWebVitals();
3. App.jsx
div
로 컴포넌트 들을 감싸주고 컴포넌트들을 사용하기만 하는 용도로 썼다.
- 지금은 내용이 많이 없어서 App.jsx 파일에서 javascript 를 사용해도 되지만 코드가 많아지면 잘 사용하지 않는다고 해서 연습해보았다.
import "./App.css";
import TodoHeader from "./components/TodoHeader";
import TodoBoard from "./components/TodoBoard";
function App() {
return (
<div className="wrap">
<TodoHeader />
<TodoBoard />
</div>
);
}
export default App;
- 화면의 가장 상단을 나타내는 부분이다.
- 기능적인 면이 딱히 없어 props를 받아올 게 없기 때문에 바로 App.jsx 에서 사용했는데, 지금 생각해보니 TodoBoard.jsx 파일에서 사용해도 좋았을 것 같다.
import React from "react";
export default function TodoHeader() {
return (
<div className="header">
<h3>My Todo List</h3>
<h3>React</h3>
</div>
);
}
5. TodoBoard.jsx
- 이 파일은 todoList 의 데이터를 가져오고, 데이터를 추가, 삭제해주는 기능을 가지고 있다.
- useState
- 첫 번째 변수는 useState() 에서 인자로 초기값을 설정해준다.
- 두 번째 변수는 첫 번째 변수의 값을 변경할 때 사용한다.
- useEffect
- 첫 번째 인자로 함수를 받고, 두 번째 인자로 배열을 받는다. 빈 배열이면 첫 번째 인자로 받은 함수가 최초 랜더링시 1회만 실행된다.
import React, { useEffect, useState } from "react";
import TodoItem from "./TodoItem";
import TodoInput from "./TodoInput";
export default function TodoBoard() {
const [todos, setTodos] = useState([]);
useEffect(() => {
fetch("db/todo.json")
.then((res) => res.json())
.then((data) => setTodos(data));
}, []);
const deleteTodoHandler = (id) => {
const newTodoList = todos.filter((todo) => todo.id !== id);
setTodos(newTodoList);
};
return (
<div>
<TodoInput todos={todos} setTodos={setTodos} />
<h1>Working.. 🔥</h1>
<div className="wrap-list">
{todos.map(
(todo) =>
todo.isDone && (
<TodoItem
key={todo.id}
id={todo.id}
title={todo.title}
text={todo.text}
isdone={todo.isDone}
deleteTodoHandler={deleteTodoHandler}
todos={todos}
setTodos={setTodos}
/>
)
)}
</div>
<h1>Done..! 🎉</h1>
<div className="wrap-list">
{todos.map(
(todo) =>
!todo.isDone && (
<TodoItem
key={todo.id}
id={todo.id}
title={todo.title}
text={todo.text}
isdone={todo.isDone}
deleteTodoHandler={deleteTodoHandler}
todos={todos}
setTodos={setTodos}
/>
)
)}
</div>
</div>
);
}
- 이 파일은 TodoList 들을 추가해주는 파일이다.
- 추가할 때 추가해줄 키 값들을 함수로 설정해둔다.
- 부모 컴포넌트 TodoBoard 에서 받아온 props 들을 사용한다.
- input 의 value 를 바꾸기 위해 useState 를 사용한다.
import React, { useState } from "react";
export default function TodoInput({ todos, setTodos }) {
const [titleValue, setTitleValue] = useState("");
const [textValue, setTextValue] = useState("");
const addItem = () => {
const newTodo = {
id: Math.random() * Math.random(),
title: titleValue,
text: textValue,
isDone: true,
};
setTodos([...todos, newTodo]);
setTitleValue("");
setTextValue("");
};
const onsubmit = (e) => {
e.preventDefault();
};
return (
<form className="form" onSubmit={onsubmit}>
<div className="form-input">
<label className="form-label" htmlFor="title">
제목{" "}
</label>
<input
className="input"
id="title"
value={titleValue}
type="text"
onChange={(event) => setTitleValue(event.target.value)}
/>
<label className="form-label" htmlFor="text">
내용{" "}
</label>
<input
className="input"
id="text"
value={textValue}
type="text"
onChange={(event) => setTextValue(event.target.value)}
/>
</div>
<button className="form-button" onClick={addItem}>
Click
</button>
</form>
);
}
7. TodoItem.jsx
- TodoList 의 각 항목들이 생성될 때와 isDone 값을 바꾸기 위한 코드이다.
- 부모 컴포넌트 TodoBoard 에서 props 를 받아와 사용한다.
import React from "react";
import { useState } from "react";
export default function TodoItem({
id,
title,
text,
deleteTodoHandler,
isdone,
todos,
setTodos,
}) {
const changeIsDone = (event) => {
const newTodos = [];
todos.forEach((todo) => {
if (todo.id === Number(event.target.value)) {
newTodos.push({ ...todo, isDone: !todo.isDone });
} else {
newTodos.push({ ...todo });
}
});
setTodos(newTodos);
};
return (
<div className="todo-item">
<div>
<h2>{title}</h2>
<p>{text}</p>
</div>
<div className="button-box">
<button onClick={() => deleteTodoHandler(id)} className="delete-button">
삭제
</button>
<button
onClick={(event) => changeIsDone(event)}
value={id}
className="isdone-button"
>
{isdone ? "취소" : "완료"}
</button>
</div>
</div>
);
}
8. 느낀점
- 리액트는 아직 너무 어렵다고 생각된다. 하지만 지금 제대로 시작한지 3일 밖에 되지 않았으니 당연하다고 생각한다.
- 처음 컴포넌트를 어떻게 나눌지 생각하고 코드를 작성하였지만 생각대로 되지 않았다.
- 오늘 완성작들을 몇 개 뽑아 튜터님이 코드 리뷰를 해주셨는데 잘한 사람들이 많아서 놀라웠다.
- 그래도 자바스크립트보다는 리액트가 더 재밌다.