티스토리 뷰

개발공부/🟦 React.js

[React] Todo-List 만들기

2022. 8. 12. 19:33
 

React – A JavaScript library for building user interfaces

A JavaScript library for building user interfaces

reactjs.org

 

 

 

 React로 Todo-List 만들기 

 

 

▶  구현 순서

 

1)  Form 개발하기

2)  리스트 표현하기

3)  유연하게 State 변경하기

4)  CSS로 꾸미기

5)  부가 기능 구현

 

 

 

 

 

▶  Form 개발하기

 

-  Form 컴포넌트 내부에서 State를 이용해 사용자의 입력을 저장/관리한다.

-  Form의 Submit 이벤트가 발생되면 부모 컴포넌트로 그 데이터를 전달한다.

// InsertForm.js

import React, { useState } from 'react';

function InsertForm({ onInsert }) {
    
    const [inputValue, setInputValue] = useState("");

    const handleSumbit = (e) => {
        e.preventDefault();
        if (typeof onInsert === "function" && inputValue) {	// onInsert가 정상적인 함수인지 확인하여 에러를 방지
            onInsert(inputValue);
        }
        setInputValue("");	// 등록 후 input의 value값 초기화
    }
      
    return (
        <form onSubmit={handleSumbit}>
            <input value={inputValue} onChange={(e) => {
                setInputValue(e.target.value)
            }} />
            <button type="submit">등록</button>
        </form>
    );
}

export default InsertForm;

 

 

 

 

 

▶  리스트 표현하기

 

Form으로부터 전달받은 값들을 리스트에 저장하고 리스트의 값을 순차적으로 화면에 출력한다.

 

-  Form으로부터 전달받은 값을 todoList array에 push하는 로직

-  todoList를 props로 전달받아 화면에 출력하는 ListView 컴포넌트

// ListView.js

import React from "react";

const ListView = ({todoList}) => {
  return (
    <div>
      <ol>
        {todoList.map((item) => {
          return (
            <li key={item.key}>
              <span>{item.value}</span>
              <button type="button">완료</button>
              <button type="button">삭제</button>
            </li>
          );
        })}
      </ol>
    </div>
  )
}

export default ListView;

 

 

 

 

 

▶  유연하게 State 변경하기

 

리스트의 값을 변경 및 삭제하는 기능을 구현한다.

 

-  ListView 내 Item의 완료 버튼을 클릭할 때 부모 컴포넌트로 완료 명령을 내릴 수 있는 이벤트 작성

-  ListView 내 Item의 삭제 버튼을 클릭할 때 부모 컴포넌트로 삭제 명령을 내릴 수 있는 이벤트 작성

-  App 컴포넌트에서 ListView의 완료 및 삭제 이벤트 핸들링 함수 작성

// ListView.js

import React from "react";

const ListView = ({todoList, onComplete, onRemove}) => {
  return (
    <div>
      <ol>
        {todoList.map((item, index) => {
          return (
            <li key={item.key}>
              <span>{item.value}</span>
              <button type="button" onClick={() => {
                  if(typeof onComplete === "function") {
                    onComplete(index);
                  }
              }}>완료</button>
              <button type="button" onClick={() => {
                  if(typeof onRemove === "function") {
                    onRemove(index);
                  }
              }}>삭제</button>
            </li>
          );
        })}
      </ol>
    </div>
  )

}

export default ListView;
// App.js

import React, { useState } from 'react';
import InsertForm from "./components/InsertForm";
import ListView from "./components/ListView";

function App() {
  const [todoList, setTodoList] = useState([]);
  
  const handleInsert = (value) => {
    setTodoList((current) => {
      const newList = [...current];
      newList.push({
        key: new Date().getTime(),
        value,
        isCompleted: false,
      });
      return newList;
    })
  }
  
  const handleComplete = (index) => {
    setTodoList((current) => {
      const newList = [...current];
      newList[index].isCompleted = true;
      return newList;
    })
  }
  
  const handleRemove = (index) => {
    setTodoList((current) => {
      const newList = [...current];
      newList.splice(index, 1);
      return newList;
    })
  }
  
  return (
    <div className="App">
        <ListView todoList={todoList} onComplete={handleComplete} onRemove={handleRemove} />
        <InsertForm onInsert={handleInsert} />
    </div>
  );
}

export default App;

 

 

 

 

 

▶  CSS로 꾸미기

 

인라인 스타일 및 CSS 파일을 활용하여 스타일을 적용한다.

 

-  ListView 컴포넌트는 별도의 CSS 파일을 import하여 style 적용하기

-  InsertForm 컴포넌트는 인라인 스타일을 입력해서 style 적용하기

// ListView.js

import "./ListView.css";

...
// ListView.css

.listview {
    display: block;
    position: relative;
    min-height: 120px;
    margin-bottom: 8px;
    background-color: #ffffff;
    padding: 16px;
    border-radius: 16px;
}

.listview ol {
    margin: 0;
    padding-left: 2em;
}

.listview ol li {
    clear: both;
    margin-bottom: 8px;
}

.listview ol li span{
    float: left;
    font-size: 16px;
}

.listview ol li.completed span {
    text-decoration: line-through;
}

.listview ol li button {
    float: right;
    margin-left: 4px;
    cursor: pointer;
    border: none;
    color: #ffffff;
    background-color: #524FA1;
    padding: 4px 8px;
}

.listview ol li button.remove {
    background-color: #FA466A;
}
// InsertForm.js

import React, { useState, useCallback } from "react";

const InsertForm = ({ onInsert }) => {
    const [inputValue, setInputValue] = useState("");
    const handleSubmit = useCallback((event) => {
        event.preventDefault();
        if(typeof onInsert === "function" && inputValue) {
            onInsert(inputValue);
        }
        setInputValue("");
    },[onInsert, inputValue])

    return (
        <form onSubmit={handleSubmit} style={{
            backgroundColor: "#ffffff",
            borderRadius: 16,
            marginBottom: 16,
            display: "flex",
            justifyContent: "space-between",
            alignItems: "center"
        }}>
            <input value={inputValue} onChange={(event) => {
                setInputValue(event.target.value);
            }} style={{
                flex: 1, 
                border: "none", 
                color: "#000000", 
                padding: "6px 12px"
            }} />
            <button type="submit" style={{
                border: "none", 
                borderRadius: 16, 
                backgroundColor: "#3ab6bc", 
                cursor: "pointer", 
                padding: "8px 16px", 
                color: "#ffffff"
            }}>등록</button>
        </form>
    )

}

export default InsertForm;

 

 

 

 

 

 

▶  부가기능 구현

 

-  [완료] 버튼 클릭 시 이펙트 추가

-  할일 개수 제한하기

 

 

1)  완료버튼 클릭 시 할일 목록에 취소선 애니메이션 추가

// ListView.css
...

.listview ol li span{
    float: left;
    font-size: 16px;
    position: relative;
}

.listview ol li span::after {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    transition: width 0.2s ease-in-out;
    width: 0%;
    height: 3px;
    background-color: #524FA1;
}

.listview ol li.completed span {
    text-decoration: line-through;
}

.listview ol li.completed span::after {
    width: 100%;
}

...

 

 

2)  할일 목록이 10개 이상일 시 메세지를 띄우고 input 입력을 비활성화

// App.js

function App() {
  ...
  
  const isLimitReached = useMemo(() => {	// isLimitReached 변수 선언
      return todoList.length >=10;
  }, [todoList]);
  
  ...
  
  return (
    <div className="App">
        <ListView todoList={todoList} onComplete={handleComplete} onRemove={handleRemove} />
        {isLimitReached 	{/* isLimitReached가 true일 때 경고메시지 띄우기 */}
        && <div style={{
            padding: "8px 16px",
            border: "1px solid #FA466A",
            backgroundColor: "#feecf0",
            color: "#FA466A",
            marginBottom: 16
            }}>
                ※ 할일 목록이 너무 많습니다.
        </div>
        }
        <InsertForm onInsert={handleInsert} disabled={isLimitReached}/>	{/* disabled이라는 props 넘겨주기 */}
    </div>
  );
}
// InsertForm.js

const InsertForm = ({ onInsert, disabled }) => {	// props에 disabled 추가

  ...
 
    return (
       ...
            <input value={inputValue} onChange={(event) => {
                setInputValue(event.target.value);
            }} style={{
                flex: 1, 
                border: "none", 
                color: "#000000", 
                padding: "6px 12px"
            }} disabled={disabled}/>	{/* input 태그에 disabled 속성 추가 */}
       ...
    )
}

 

 

 

 


 이 글은 엘리스의 AI트랙 5기 강의를 들으며 정리한 내용입니다.

반응형

'개발공부 > 🟦 React.js' 카테고리의 다른 글

[React] SPA와 라우팅  (0) 2022.08.13
[React] React 스타일링  (0) 2022.08.12
[React] Hooks  (0) 2022.08.11
[React] 이벤트 처리  (0) 2022.08.11
[React] JSX와 컴포넌트  (0) 2022.08.10
프로필사진
개발자 삐롱히

프론트엔드 개발자 삐롱히의 개발 & 공부 기록 블로그