안랩샘아카데미 React로 시작하는 프론트엔드 8주차 정리

구나영·2024년 5월 17일

todolistapp

TodoItem.jsx input쪽에서 checked를 사용해서 발생하는 경고
를 없애기위해 onChange나 readOnly 속성을 줘야해서
임시로 readOnly 속성을 준다.

TodoItem.jsx

import "./TodoItem.css"

const TodoItem = ({id, isDone, content, date}) => {
    return (
     <div className="TodoItem">
    <input readOnly checked={isDone} type="checkbox" />
<div className="content ">{content}</div>
<div className="date">{new Date(date).toLocaleDateString()}</div>
<button>삭제</button>
</div>
);
};

export default TodoItem;

List.jsx에서 구분하는 key값이 없어서 발생하는 경고를 없애기위해
TodoItem에 key로 todo.id를 전달해준다.

import "./List.css";
import TodoItem from "./TodoItem";

const List = ({ todos }) => {
    return (
    <div className="List">
        <h4>🎈Todo List...🎈</h4>
        <input placeholder="검색어를 입력하세요" />
        <div className="todos_wrapper">
            {todos.map((todo)=>{
            return <TodoItem key={todo.id} {...todo}/>;
        })}
        </div>
    </div>
    );
};

export default List;

수정후 개발자도구로 List를 확인해보면 key가 생성된 걸 확인할 수 있다.

List에 검색기능 추가하기(검색어를 관리하는 State 생성하고
input에 value로 search주고 onChange이벤트에 onChangeSearch이벤트핸들러
등록하고 만들기, 필터링된 todo를 반환해주는 getFilteredData 만들기

List.jsx

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

const List = ({ todos, onUpdate, onDelete }) => {
  const [search, setSearch] = useState("");
  const onChangeSearch = (e) => {
    setSearch(e.target.value);
  };

  const getFilteredData = () => {
    if (search === "") {
      return todos; //search가 비어있으면 전체를 리턴
    }
    return todos.filter((todo) => {
      todo.content.includes(search); //필터메소드를 사용해서
      //search를 포함하는 content만 리턴하도록한다.
    }); 
  };

  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;

List.jsx
getFilteredData에서 소문자,대문자 모두 검색할수있도록 content와 search
toLowerCase로 수정하기

const getFilteredData = () => {
    if (search === "") { 
      return todos; //search가 비어있으면 전체를 리턴
    }
    return todos.filter((todo) => {
      todo.content.toLowerCase().includes(search.toLowerCase());
      //필터메소드를 사용해서 search를 포함하는 content만 리턴하도록한다.
    });
  };

수정기능 만들기
App.jsx

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

const testData = [
  {
  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(testData);
  const idRef = useRef(3);

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

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

  };

  const onUpdate = (targetID) => {
    setTodos(todos.map((todo)=>{
      if(todo.id === targetID){
        return {...todo, isDone: !todo.isDone};
      }
      return todo;
    }));
  };

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

export default App;

onUpdate를 App에서 List에 전달하고 List에서 TodoItem으로 전달한다.
List.jsx

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

const List = ({ todos, onUpdate }) => {
  const [search, setSearch] = useState("");
  const onChangeSearch = (e) => {
    setSearch(e.target.value);
  };

  const getFilteredData = () => {
    if (search === "") { 
      return todos; //search가 비어있으면 전체를 리턴
    }
    return todos.filter((todo) => {
      todo.content.toLowerCase().includes(search.toLowerCase());
      //필터메소드를 사용해서 search를 포함하는 content만 리턴하도록한다.
    });
  };

  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} onUpdate=
            {onUpdate}/>;
        })}
        </div>
    </div>
    );
};

export default List;

readOnly 삭제하고 onChange 이벤트 등록하기

TodoItem.jsx

import "./TodoItem.css"

const TodoItem = ({id, isDone, content, date, onUpdate }) => {

    const onChangeCheckBox = () => {
        onUpdate(id);
    };
    return (
     <div className="TodoItem">
    <input onChange={onChangeCheckBox} checked={isDone} type="checkbox" />
<div className="content">{content}</div>
<div className="date">{new Date(date).toLocaleDateString()}</div>
<button>삭제</button>
</div>
);
};

export default TodoItem;

체크하면 isDone 값이 true로 바뀌는걸 확인할 수 있다.

삭제기능 추가하기

App.jsx

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

const testData = [
  {
  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(testData);
  const idRef = useRef(3);

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

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

  };

  const onUpdate = (targetID) => {
    setTodos(todos.map((todo)=>{
      if(todo.id === targetID){
        return {...todo, isDone: !todo.isDone};
      }
      return todo;
    }));
  };

  const onDelete = (targetID) =>{
    setTodos(todos.filter((todo)=> todo.id !==
    targetID));
  };

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

export default App;

List.jsx

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

const List = ({ todos, onUpdate, onDelete }) => {
  const [search, setSearch] = useState("");
  const onChangeSearch = (e) => {
    setSearch(e.target.value);
  };

  const getFilteredData = () => {
    if (search === "") { 
      return todos; //search가 비어있으면 전체를 리턴
    }
    return todos.filter((todo) => {
      todo.content.toLowerCase().includes(search.toLowerCase());
      //필터메소드를 사용해서 search를 포함하는 content만 리턴하도록한다.
    });
  };

  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} onUpdate=
            {onUpdate} onDelete={onDelete}/>;
        })}
        </div>
    </div>
    );
};

export default List;

TodoItem.jsx

import "./TodoItem.css"

const TodoItem = ({id, isDone, content, date, onUpdate, onDelete }) => {

    const onChangeCheckBox = () => {
        onUpdate(id);
    };

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

    return (
     <div className="TodoItem">
    <input onChange={onChangeCheckBox} checked={isDone} type="checkbox" />
<div className={`content ${isDone ? 'done' : ''}`}>{content}</div>
<div className="date">{new Date(date).toLocaleDateString()}</div>
<button onClick={onClickDeleteButton}>삭제</button>
</div>
);
};

export default TodoItem;

최적화

0개의 댓글