[React] 8. 프로젝트2 - 투두리스트

DonaDona·2024년 9월 29일

8.1) 프로젝트 소개 및 준비

프로젝트 생성

npm create vite@latest
npm i
npm run dev

초기화

  • App.css, index.css 파일 내 코드 삭제
  • 안쓰는 리소스 삭제 (.svg)
  • App.jsx 초기화 - 리턴 값 빈태그
  • main.jsx 초기화 - 리턴값 StrictMode 컴포넌트 삭제

8.2) UI 구현하기

CSS 관련 참고 링크

  • Header, Editor, List, TodoItem 으로 구성

8.3) 기능 구현 준비하기

  • 추가, 수정, 삭제, 검색 기능 -> TodoItem의 데이터를 state로 저장해야함
  • 초기값 mockData 생성 및 설정

App.jsx

import { useState } from "react";
import "./App.css";
import Header from "./components/Header";
import Editor from "./components/Editor";
import List from "./components/List";

const mockData = [
  {
    id: 0,
    isDone: false,
    content: "React 공부하기",
    date: new Date().getTime(),
  },
  {
    id: 1,
    isDone: false,
    content: "빨래하기",
    date: new Date().getTime(),
  },
  {
    id: 2,
    isDone: false,
    content: "노래연습하기",
    date: new Date().getTime(),
  },
];

function App() {
  const [todos, setTodos] = useState([mockData]);

  return (
    <div className="App">
      <Header />
      <Editor />
      <List />
    </div>
  );
}

export default App;

8.4) Create - 투두 추가하기

App.jsx

import { useState, useRef } from "react";
import "./App.css";
import Header from "./components/Header";
import Editor from "./components/Editor";
import List from "./components/List";

const mockData = [
  {
    id: 0,
    isDone: false,
    content: "React 공부하기",
    date: new Date().getTime(),
  },
  {
    id: 1,
    isDone: false,
    content: "빨래하기",
    date: new Date().getTime(),
  },
  {
    id: 2,
    isDone: false,
    content: "노래연습하기",
    date: new Date().getTime(),
  },
];

function App() {
  const [todos, setTodos] = useState(mockData);
  const idRef = useRef(3);

  const onCreate = (content) => {
    const newTodo = {
      id: idRef.current++,
      isDone: false,
      content: content,
      date: new Date().getTime(),
    };

    setTodos([newTodo, ...todos]);
  };

  return (
    <div className="App">
      <Header />
      <Editor onCreate={onCreate} />
      <List />
    </div>
  );
}

export default App;

Editor.jsx

import "./Editor.css";
import { useState, useRef } from "react";

const Editor = ({ onCreate }) => {
  const [content, setContent] = useState("");
  const contentRef = useRef();
  const onChangeContent = (e) => {
    setContent(e.target.value);
  };

  const onSubmit = () => {
    if (content === "") {
      contentRef.current.focus();
      return;
    }
    onCreate(content);
    setContent("");
  };

  const onKeydown = (e) => {
    if (e.keyCode === 13) onSubmit();
  };

  return (
    <div className="Editor">
      <input
        ref={contentRef}
        value={content}
        onChange={onChangeContent}
        onKeyDown={onKeydown}
        placeholder="새로운 Todo..."
      />
      <button onClick={onSubmit}>추가</button>
    </div>
  );
};

export default Editor;

8.5) Read - 투두리스트 렌더링하기

List.jsx

import "./List.css";
import TodoItem from "./TodoItem";
import { useState } from "react";

const List = ({ todos }) => {
  const [search, setSearch] = useState("");

  const onChangeSearch = (e) => {
    setSearch(e.target.value);
  };

  const getFilteredData = () => {
    if (search === "") {
      return todos;
    }
    return todos.filter((todo) =>
      todo.content.toLowerCase().includes(search.toLocaleLowerCase())
    );
  };

  const filteredTodos = getFilteredData();

  return (
    <div className="List">
      <h4>Todo List 📌</h4>
      <input
        value={search}
        onChange={onChangeSearch}
        placeholder="검색어를 입력하세요"
      />
      <div className="todos_wrapper">
        {filteredTodos.map((todo) => {
          return <TodoItem key={todo.id} {...todo} />;
        })}
      </div>
    </div>
  );
};

export default List;

return <TodoItem key={todo.id} {...todo} />;

  • 리스트로 렌더링하는 코드에 에러 발생
  • 리액트에서는 내부적으로 리스트 형태로 렌더링된 컴포넌트들이나 또는 어떤 요소들을 서로 구분할 때 각각의 요소를 Key라는 프롭을 통해서 구분함
  • 따라서 리스트로 어떠한 컴퍼넌트를 렌더링하고 있을 때는 모든 아이템 컴포넌트에 key라는 프롭을 고유한 값으로 전달해줘야한다.

8.6) Update - 투두 수정하기

Trouble Shooting

setTodos(
  todos.map((todo) =>
    todo.id === targetId ? { ...todo, isDone: !todo.isDone } : todo
  )
);

setTodos(
  todos.map((todo) =>
    {todo.id === targetId ? { ...todo, isDone: !todo.isDone } : todo;}
  )
);

중괄호로 감쌀 경우 새로운 객체 생성의 의미가 되어버려 의도와 다른 코드가 된다. 주의.

8.7) Delete - 투두 삭제하기

App.jsx

...
const onDelete = (targetId) => {
  // 인수 : todos 배열에서 targetId와 일치하는 id를 갖는 요소만 삭제한 새로운 배열
  setTodos(todos.filter((todo) => todo.id != targetId));
};
...

<div className="App">
  <Header />
  <Editor onCreate={onCreate} />
    <List todos={todos} onUpdate={onUpdate} onDelete={onDelete} />
</div>
...

TodoItem.jsx

...
const onClickDeleteButton = () => {
  onDelete(id);
};
...

<button onClick={onClickDeleteButton}>삭제</button>
...
profile
기록용 공부용 개발 블로그

0개의 댓글