Learning React(10. React Todo List 앱)

min seung moon·2021년 8월 10일
0

Learning React

목록 보기
10/15

1. Todo List

01. 시작하기

-1. create-react-app todolist

  • public과 src 폴더는 다 지우고 깔끔하게 시작

-2. public / index.html

<!DOCTYPE html>
<html>

<head>
	<title>Todo List</title>
</head>

<body>
	<div id="container">
    </div>
</body>

</html>

-3. src / index.css

body {
  padding : 50px;
  background-color: #66CCFF;
  font-family: sans-serif;
}

#container {
  display: flex;
  justify-content: center;
}

-4. src/ index.js

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";

ReactDOM.render(
  <div>
    <p>Hello!</p>
  </div>,
  document.querySelector("#container")
);

02. 초기 UI 제작

-1. src / TodoList.js

  • 이 곳에서 TodoList의 데이터를 입력 받을 예정이다!
import React, { Component } from "react";

class TodoList extends Component {
  render() {
    return (
      <div className="todolistMain">
        <div className="header">
          <form>
            <input type="text" placeholder="enter task" />
            <button type="submit">add</button>
          </form>
        </div>
      </div>
    );
  }
}

export default TodoList;

-2. src / index.js

  • TodoList.js 추가
import React from "react";
import ReactDOM from "react-dom";
import TodoList from "./TodoList";
import "./index.css";

ReactDOM.render(
  <div>
    <TodoList />
  </div>,
  document.querySelector("#container")
);

03. 앱의 기능적인 부분 추가

    1. 아이템 추가
    1. 아이템 표시
    1. 스타일 적용
    1. 아이템 삭제
    1. 아이템 추가와 삭제 시 애니메이션 적용

-1. 아이템 추가

  • src / TodoList.js
    • form의 submit 이벤트로 아이템을 저장하는 이벤트를 적용시켜준다
    • 생성자 state에 item을 저장할 items 배열을 생성한다
import React, { Component } from "react";

class TodoList extends Component {

  constructor(props) {
    super(props);

    this.state = {
      items : []
    };
    this.addItem = this.addItem.bind(this);
  }

  addItem(e) {

  }

  render() {
    return (
      <div className="todolistMain">
        <div className="header">
          <form onSubmit={this.addItem}>
            <input type="text" placeholder="enter task" />
            <button type="submit">add</button>
          </form>
        </div>
      </div>
    );
  }
}

export default TodoList;

  • src / TodoList.js
    • DOM에 직접 접근해 속성을 다루게하지 않고, 대신 ref를 통해 사용하게 하는 구멍을 제공해준다
    • input 엘리먼트의 참조를 _inputElement라는 속성에 저장
      • 다른 컴포넌트 안의 input 엘리먼트에 접근하고 싶으면 _inputLement를 통해 할수 있게 된다
  • addItem 함수가 실행이 되면 input 태그에 값이 있다면 객체로 데이터를 만들어서 최종적으로 items에 입력한다
import React, { Component } from "react";

class TodoList extends Component {
  constructor(props) {
    super(props);

    this.state = {
      items: [],
    };
    this.addItem = this.addItem.bind(this);
  }

  addItem(e) {
    var itemArray = this.state.items;

    if(this._inputElement.value !== "") {
      itemArray.unshift({
        text : this._inputElement.value,
        key : Date.now()
      });

      this.setState({
        items : itemArray
      });

      this._inputElement.value = "";
    }
    console.log(itemArray);
    
    e.preventDefault();
  }

  render() {
    return (
      <div className="todolistMain">
        <div className="header">
          <form onSubmit={this.addItem}>
            <input
              ref={(a) => (this._inputElement = a)}
              type="text"
              placeholder="enter task"
            />
            <button type="submit">add</button>
          </form>
        </div>
      </div>
    );
  }
}

export default TodoList;


-2. 아이템 표시

  • 이제 콘솔이 아닌 화면에서 직접 확인할 수 있게 해주자
  • src / TodoItems.js
    • createTasks 함수는 넘겨받은 item 배열의 item을 하나씩 받아서 li 태그로 만들어서 반환해준다
    • todoEntries는 TodoList에서 넘겨받은 item 배열을 받는 변수다
    • listItems은 todoEntries의 item 배열을 createTasks로 li로 만든 태그 배열이다
import React, { Component } from "react";

class TodoItems extends Component {
  createTasks(item) {
    return <li key={item.key}>{item.text}</li>;
  }

  render() {
    var todoEntries = this.props.entries;
    var listItems = todoEntries.map(this.createTasks);

    return <ul className="theList">{listItems}</ul>;
  }
}

export default TodoItems;
  • src / TodoList.js
import React, { Component } from "react";
import TodoItems from "./TodoItems";

class TodoList extends Component {
  constructor(props) {
    super(props);

    this.state = {
      items: [],
    };
    this.addItem = this.addItem.bind(this);
  }

  addItem(e) {
    var itemArray = this.state.items;

    if (this._inputElement.value !== "") {
      itemArray.unshift({
        text: this._inputElement.value,
        key: Date.now(),
      });

      this.setState({
        items: itemArray,
      });

      this._inputElement.value = "";
    }
    console.log(itemArray);

    e.preventDefault();
  }

  render() {
    return (
      <div className="todolistMain">
        <div className="header">
          <form onSubmit={this.addItem}>
            <input
              ref={(a) => (this._inputElement = a)}
              type="text"
              placeholder="enter task"
            />
            <button type="submit">add</button>
          </form>
        </div>
        <TodoItems entries={this.state.items} />
      </div>
    );
  }
}

export default TodoList;



-3. 스타일 적용

  • src / TodoList.css
.todolistMain .header input {
  padding: 10px;
  font-size: 16px;
  border: 2px solid #FFF;
  width: 165px;
}

.todolistMain .header button {
  padding: 10px;
  font-size: 16px;
  margin: 10px;
  margin-right: 0px;
  background-color: #0066FF;
  color: #FFF;
  border: 2px solid #0066FF;
}

.todolistMain .theList {
  list-style: none;
  padding-left: 0;
  width: 250px;
}

.todolistMain .theList li {
  color: #333;
  background-color: rgba(255, 255, 255, .5);
  padding: 15px;
  margin-bottom: 15px;
  border-radius: 5px;
}


-4. 아이템 삭제

  • 아이템을 삭제를 할려면 items를 갖고 있는 ItemList의 state와 연결되어야 한다 이제 그 부분을 할 예정이다!

  • createTasks return에서 li태그에 onClick 이벤트를 추가한다

    • 이벤트는 arrow function을 사용하는데 이유는 기본이벤트 인자를 유지하면서 전달 할 수 있게 해주기 때문이다(arrow function의 특성)
  • src / TodoList.js

    • deleteItems 함수를 생성해주고 filterItems에 items에서 filter한 값을 저장 후 items에 입력해준다
import React, { Component } from "react";
import TodoItems from "./TodoItems";
import "./TodoList.css";

class TodoList extends Component {
  constructor(props) {
    super(props);

    this.state = {
      items: [],
    };
    this.addItem = this.addItem.bind(this);
    this.deleteItem = this.deleteItem.bind(this);
  }

  addItem(e) {
    var itemArray = this.state.items;

    if (this._inputElement.value !== "") {
      itemArray.unshift({
        text: this._inputElement.value,
        key: Date.now(),
      });

      this.setState({
        items: itemArray,
      });

      this._inputElement.value = "";
    }

    e.preventDefault();
  }

  deleteItem(key) {
    var filterItems = this.state.items.filter(function (item) {
      return item.key !== key;
    });

    this.setState({
      items: filterItems,
    });
  }

  render() {
    return (
      <div className="todolistMain">
        <div className="header">
          <form onSubmit={this.addItem}>
            <input
              ref={(a) => (this._inputElement = a)}
              type="text"
              placeholder="enter task"
            />
            <button type="submit">add</button>
          </form>
        </div>
        <TodoItems entries={this.state.items} delete={this.deleteItem} />
      </div>
    );
  }
}

export default TodoList;


  • src / TodoItems.js
    • delete라는 함수를 생성하여 key 값을 받고 이 함수에서 TodoList에서 전달받은 delete 함수를 동작시킨다
import React, { Component } from "react";

class TodoItems extends Component {
  constructor(props) {
    super(props);

    this.createTasks = this.createTasks.bind(this);
  }

  delete(key) {
    this.props.delete(key);
  }

  createTasks(item) {
    return (
      <li onClick={() => this.delete(item.key)} key={item.key}>
        {item.text}
      </li>
    );
  }

  render() {
    var todoEntries = this.props.entries;
    var listItems = todoEntries.map(this.createTasks);

    return <ul className="theList">{listItems}</ul>;
  }
}

export default TodoItems;

  • TodoList.css
.todolistMain .header input {
  padding: 10px;
  font-size: 16px;
  border: 2px solid #FFF;
  width: 165px;
}

.todolistMain .header button {
  padding: 10px;
  font-size: 16px;
  margin: 10px;
  margin-right: 0px;
  background-color: #0066FF;
  color: #FFF;
  border: 2px solid #0066FF;
}

.todolistMain .theList {
  list-style: none;
  padding-left: 0;
  width: 250px;
}

.todolistMain .theList li {
  color: #333;
  background-color: rgba(255, 255, 255, .5);
  padding: 15px;
  margin-bottom: 15px;
  border-radius: 5px;

  transition: background-color .2s ease-out;
}

.todolistMain .theList li:hover {
  background-color: pink;
  cursor: pointer;
}



-5. 애니메이션

  • 아이템을 추가하거나 삭제할 때 더욱 자련스럽게 보이게 하는 몇가지 애니메이션을 추가해보자
  • 애니메이션은 CSS나 requestAnimationFrame, 웹 애니메이션 API 등 다양한 방법이 있지만 이미 존재하는 엘리먼트에 애니메이션을 적용할 때 약간의 제약이 있다
    • 이는 리액트가 DOM으로부터 제거되려고 하는 엘리먼트의 생명주기를 완전히 관장하기 때문이다
    • 물론 생명주기 메서드 일부를 재정의해 엘리먼트 제거를 가로채고, 거기에 애니메이션 로직을 넣을 수도 있지만 복잡하고 번거롭기에 이번에는 안하고
    • 이번에는 플립 무브 라이브러리를 사용할 예정이다
    • npm i -S react-flip-move
    • import FlipMove from 'react-flip-move
  • src / TodoItems.js
import React, { Component } from "react";
import FlipMove from "react-flip-move";

class TodoItems extends Component {
  constructor(props) {
    super(props);

    this.createTasks = this.createTasks.bind(this);
  }

  delete(key) {
    this.props.delete(key);
  }

  createTasks(item) {
    return (
      <li onClick={() => this.delete(item.key)} key={item.key}>
        {item.text}
      </li>
    );
  }

  render() {
    var todoEntries = this.props.entries;
    var listItems = todoEntries.map(this.createTasks);

    return (
      <ul className="theList">
        <FlipMove duration={250} easing="ease-out">
          {listItems}
        </FlipMove>
      </ul>
    );
  }
}

export default TodoItems;

profile
아직까지는 코린이!

0개의 댓글

관련 채용 정보