스파르타코딩클럽 내일배움캠프 TIL28

한재창·2022년 12월 7일
0

리액트 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;

4. TodoHeader.jsx

  • 화면의 가장 상단을 나타내는 부분이다.
  • 기능적인 면이 딱히 없어 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") // 1. todo.json 파일의 데이터를 가져온다음
      .then((res) => res.json()) // 2. json 형식으로 변환해주고
      .then((data) => setTodos(data)); // 3. data를 todos에 담아준다.
  }, []);

  const deleteTodoHandler = (id) => {
    // todo.id 가 인자로 받은 id와 같지 않은 것만 filter 해준다.
    // 즉, 삭제 버튼이 눌리지 않은 아이디만 배열로 재설정
    const newTodoList = todos.filter((todo) => todo.id !== id);
    // filter 된 값을 todos로 넣어준다.
    setTodos(newTodoList);
  };

  return (
    <div>
      // TodoInput에 todos, setTodos를 넘겨준다.
      <TodoInput todos={todos} setTodos={setTodos} />
      <h1>Working.. 🔥</h1>
      // todo.isDone 의 값이 true면 Working 밑에 출력
      <div className="wrap-list">
        // map 메서드를 이용해 TodoItem 출력하고, props를 넘겨준다.
        {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>
      // todo.isDone 의 값이 false면 Working 밑에 출력
      <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>
  );
}

6. TodoInput.jsx

  • 이 파일은 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]); // 기본 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)}
        /> // input의 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 = [];
    // 만약 todo.id와 버튼을 누른 todo의 id가 같다면
    // newTodos에 원래 todo과 isDone 바뀐 todo를 넣는다.
    // 아니라면 원래 todo를 push 해준다.
    todos.forEach((todo) => {
      if (todo.id === Number(event.target.value)) {
        newTodos.push({ ...todo, isDone: !todo.isDone });
      } else {
        newTodos.push({ ...todo });
      }
    });
    // todos에 newTodos를 넣어준다.
    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일 밖에 되지 않았으니 당연하다고 생각한다.
  • 처음 컴포넌트를 어떻게 나눌지 생각하고 코드를 작성하였지만 생각대로 되지 않았다.
  • 오늘 완성작들을 몇 개 뽑아 튜터님이 코드 리뷰를 해주셨는데 잘한 사람들이 많아서 놀라웠다.
  • 그래도 자바스크립트보다는 리액트가 더 재밌다.
profile
취준 개발자

0개의 댓글