본문 바로가기
매일 해내는 개발/React

[React] 리액트로 todo리스트 만들기

by 해야지 2022. 12. 7.
반응형

컴포넌트: Todo, Button

추가기능: 가로스크롤

id를 Date.now()로 지정해 id 겹침현상 제거

 

완료된 모습

App.js

import React, { useState } from 'react';
import './App.css';
import Todo from './components/Todo';
import CustomBtn from './components/CustomBtn';

function App() {
  const [todo, setTodo] = useState([
    {
      id: Date.now(),
      title: 'todotest',
      contents: '리액트기초를 공부해봅시다.',
    },
    {
      id: Date.now() + 1,
      title: 'todotest222',
      contents: '리액트기초를 공부해봅시다.',
    },
  ]);

  const [doneTodo, setDoneTodo] = useState([]);
  const [title, setTitle] = useState('');
  const [contents, setContents] = useState('');
  const addTodoHandler = () => {
    const newTodo = {
      id: Date.now(),
      title: title,
      contents: contents,
    };
    if (title === '' && contents === '') alert('내용을 추가하세요');
    else setTodo([...todo, newTodo]);
  };

  // 할일삭제기능
  const deleteTodoHandler = (id) => {
    setTodo(todo.filter((t) => t.id !== id));
  };

  // 완료삭제기능
  const deleteDoneTodoHandler = (id) => {
    setDoneTodo(doneTodo.filter((dt) => dt.id !== id));
  };

  // 할일완료기능
  const doneTodoHandler = (dt) => {
    const newDoneTodo = {
      id: dt.id,
      title: dt.title,
      contents: dt.contents,
    };
    setDoneTodo([...doneTodo, newDoneTodo]);
    setTodo(todo.filter((t) => t.id !== dt.id));
  };

  // 완료취소기능
  const doneCancelHandler = (t) => {
    const newTodo = {
      id: t.id,
      title: t.title,
      contents: t.contents,
    };
    setTodo([...todo, newTodo]);
    setDoneTodo(doneTodo.filter((dt) => t.id !== dt.id));
  };

  return (
    <div className='Outer'>
      <div className='InputArea'>
        제목
        <input
          className='Input'
          value={title}
          onChange={(e) => setTitle(e.target.value)}
        ></input>
        내용
        <input
          className='Input'
          value={contents}
          onChange={(e) => setContents(e.target.value)}
        ></input>
        <CustomBtn buttonColor='#6B615F' onClick={addTodoHandler}>
          추가하기
        </CustomBtn>
      </div>
      <div className='Outer'>
        <h2 className='title'>✅Todo</h2>
        <div className='Scroll'>
          {todo.map((todo) => {
            return (
              <Todo
                todo={todo}
                title={todo.title}
                key={todo.id}
                contents={todo.contents}
                firstHandler={deleteTodoHandler}
                secondHandler={doneTodoHandler}
                firstButton='삭제하기'
                secondButton='완료하기'
                color='#FFB8B0'
              />
            );
          })}
        </div>
        <h2 className='title'>🐾Done</h2>
        <div className='Scroll'>
          {doneTodo.map((doneTodo) => {
            return (
              <Todo
                todo={doneTodo}
                title={doneTodo.title}
                key={doneTodo.id}
                contents={doneTodo.contents}
                firstHandler={deleteDoneTodoHandler}
                secondHandler={doneCancelHandler}
                firstButton='삭제하기'
                secondButton='취소하기'
                color='#5DA37F'
              />
            );
          })}
        </div>
      </div>
    </div>
  );
}

export default App;

./components/Todo.js

import React from 'react';
import '../App.js';
import CustomBtn from './CustomBtn.js';

function Todo(props) {
  const { title, contents, todo, firstButton, secondButton, color } = props;
  //두번째 버튼이 취소하기면 버튼 색상 변경하기
  if (secondButton === '취소하기')
    return (
      <div style={{ borderColor: color }} className='Box'>
        <p>{title}</p>
        <p>{contents}</p>
        <p>
          <CustomBtn
            buttonColor='#c1e8d4'
            onClick={() => props.firstHandler(todo.id)}
          >
            {firstButton}
          </CustomBtn>
          <CustomBtn
            buttonColor='#c1e8d4'
            onClick={() => props.secondHandler(todo)}
          >
            {secondButton}
          </CustomBtn>
        </p>
      </div>
    );
  return (
    <div style={{ borderColor: color }} className='Box'>
      <p>{title}</p>
      <p>{contents}</p>
      <p>
        <CustomBtn
          buttonColor='#eb948a'
          onClick={() => props.firstHandler(todo.id)}
        >
          {firstButton}
        </CustomBtn>
        <CustomBtn
          buttonColor='#eb948a'
          onClick={() => props.secondHandler(todo)}
        >
          {secondButton}
        </CustomBtn>
      </p>
    </div>
  );
}

export default Todo;

./components/CustomBtn.js

import React from 'react';
import '../App.js';

const CustomBtn = (props) => {
  const { onClick, children, buttonColor } = props;
  return (
    <button
      style={{ backgroundColor: buttonColor }}
      onClick={onClick}
      className='Button'
    >
      {children}
    </button>
  );
};

export default CustomBtn;

App.css

@font-face {
  font-family: 'twayair';
  src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_tway@1.0/twayair.woff')
    format('woff');
  font-weight: normal;
  font-style: normal;
}
@font-face {
  font-family: 'Pretendard-Regular';
  src: url('https://cdn.jsdelivr.net/gh/Project-Noonnu/noonfonts_2107@1.1/Pretendard-Regular.woff')
    format('woff');
  font-weight: 400;
  font-style: normal;
}

* {
  /* justify-content: center; */
  align-items: center;
  font-family: 'Pretendard-Regular';
}
.Outer {
  padding-top: 30px;
  width: 800px;
  margin: 0 auto 0 auto;
}
.Scroll {
  overflow: auto;
  white-space: nowrap;
  margin: 0 auto;
  display: flex;
}
.InputArea {
  display: flex;
  padding: 10px auto 0 auto;
  gap: 10px;
  /* width: 800px; */
  height: 50px;
  background-color: #cbd2d3;
  border-radius: 10px;
  align-items: center;
  justify-content: center;
}
.Input {
  border: 0px;
  height: 25px;
}

.title {
  font-family: 'twayair';
}

.Box {
  width: 300px;
  border: 3px solid;
  border-radius: 10px;
  height: 150px;
  align-self: center;
  justify-content: center;
  align-items: center;
  margin: 10px;
  display: flex;
  flex-direction: column;
}

.Button {
  /* color: white; */
  border: 0px;
  border-radius: 50px;
  height: 30px;
  margin: 0 10px;
}

 

vercel을 이용한 배포

https://react-todo-teal-phi.vercel.app/

 

React App

 

react-todo-teal-phi.vercel.app

반응형

댓글