본문 바로가기

카테고리 없음

Study_240517 (React 학습) TodoList 학습

 

 

// 리액트로 개발한 앱은 컴포넌트라는 조각을 구성됨
// -> UI구축을 훨씬 쉽게 만들어줌
// 컴포넌트를 생성하고 UI 요소를 컴포넌트 내부에서 JSX통해 선언
// -> 리액트가 반응 -> 리액트 컴포넌트가 '선언체'라는 개념이 아주 중요

// 리액트에서 랜더링은 현재 props와 state의 상태에 기초하여
// UI구성을 어떻게 구성할지 컴포넌트에게 요청하는 작업을 의미
// triggering -> rendering -> commit 순으로 진행
// 브라우저 랜더링은 다른 프로세스로 페인팅 이라고도 부름

import Layout from "./components/Layout";
import TodoContainer from "./components/todo/TodoContainer";

const App = () => {
  return (
    <Layout>
      <TodoContainer />
    </Layout>
  );
};

export default App;

 

const Layout = ({ children }) => {
  return <main className="layout">{children}</main>;
};

export default Layout;

 

import TodoForm from "./TodoForm";
import TodoList from "./TodoList";
import ExTodoForm from "./ExTodoForm";
import { useState } from "react";

const TodoContainer = () => {
  const [todos, setTodos] = useState([
    {
      id: 1,
      title: "ok",
      content: "content111",
      isDone: false,
    },
    {
      id: 2,
      title: "ok2",
      content: "content222",
      isDone: true,
    },
  ]);

  const workingTodos = todos.filter((todo) => !todo.isDone);
  const doneTodos = todos.filter((todo) => todo.isDone);

  return (
    <section>
      <h1 className="title">Todo List</h1>
      <ExTodoForm setTodos={setTodos} />
      <TodoList title="Working" todos={workingTodos} setTodos={setTodos} />
      <TodoList title="Done" todos={doneTodos} setTodos={setTodos} />
    </section>
  );
};

export default TodoContainer;

 

// 비제어 컴포넌트
const ExTodoForm = ({ setTodos }) => {
  const onSubmit = (e) => {
    e.preventDefault();
    const formData = new FormData(e.target);
    const title = formData.get("title");
    const content = formData.get("content");

    // 시작과 끝에 공백 문자를 제거 -> trim()
    if (!title.trim() || !content.trim()) {
      return alert("no!");
    }

    const nextTodo = {
      id: crypto.randomUUID(),
      title,
      content,
      isDone: false,
    };

    // useState의 비동기성
    setTodos((prevTodos) => [nextTodo, ...prevTodos]);

    e.target.reset();
  };

  return (
    <div>
      <form onSubmit={onSubmit}>
        <input type="text" placeholder="제목" name="title" />
        <input type="text" placeholder="내용" name="content" />
        <button type="submit">등록</button>
      </form>
    </div>
  );
};
export default ExTodoForm;

 

// 제어 , 비제어 컴포넌트
// 제어 컴포넌트 : 개발자가 매순간 개입하므로 코드가 많아짐
// 비제어 '' : 한 순간만 개입하여 코드가 단순해짐

// 제어 컴포넌트
import { useState } from "react";

const TodoForm = () => {
  const [title, setTitle] = useState("");
  const [content, setContent] = useState("");

  const onSubmit = (e) => {
    e.preventDefault();
    console.log(title, content);
  };

  return (
    <div>
      <form onSubmit={onSubmit}>
        <input
          type="text"
          placeholder="제목"
          value={title}
          onChange={(event) => setTitle(event.target.value)}
        />
        <input
          type="text"
          placeholder="내용"
          value={content}
          onChange={(event) => setContent(event.target.value)}
        />
        <button type="submit">등록</button>
      </form>
    </div>
  );
};

export default TodoForm;

 

import TodoItem from "./TodoItem";

const TodoList = ({ title, todos, setTodos }) => {
  return (
    <div>
      <h2 className="todoListTitle">{title}</h2>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>
            <TodoItem todo={todo} setTodos={setTodos} />
          </li>
        ))}
      </ul>
    </div>
  );
};

export default TodoList;

 

const TodoItem = ({ todo, setTodos }) => {
  const { id, title, content, isDone } = todo;

  const deleteTodo = () => {
    setTodos((prev) => prev.filter((todo) => todo.id !== id));
  };

  const toggleTodo = () => {
    setTodos((prev) =>
      prev.map((todo) =>
        todo.id === id ? { ...todo, isDone: !todo.isDone } : todo
      )
    );
  };

  return (
    <div className="todoCard">
      <h3 className="todoTitle">{todo.title}</h3>
      <p>{todo.content}</p>
      <div>
        <button onClick={toggleTodo}>{isDone ? "취소" : "완료"}</button>
        <button onClick={deleteTodo}>삭제</button>
      </div>
    </div>
  );
};

export default TodoItem;

 

.layout {
  max-width: 1200px;
  min-width: 800px;
  margin: 0 auto;
}

.title {
  font-size: 2rem;
  font-weight: bold;
  margin: 1rem 0;
}

.todoListTitle {
  font-size: 1.5rem;
  font-weight: bold;
  margin: 1rem 0;
}

.todoCard {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1rem;
  border: 1px solid #ccc;
}

.todoTitle {
  font-size: 1.5rem;
  font-weight: bold;
}