프론트엔드/React

TODO LIST 만들기 및 useMemo() 개념

짱뚱짱 2024. 9. 9. 11:44

📢 useMemo()

👉🏻 리액트에서 컴포넌트 성능을 최적화 하는데 사용되는 훅(Hook).
👉🏻 memo => memoization의 약자로, "메모리에 저장하다"라는 의미.

👉🏻 동일한 계산을 반복해야 할 때, 이전의 값을 메모리에 저장함으로써 반복수행을 제거하여 프로그램 실행 속도를
빠르게 해주는 기술

👉🏻 컴포넌트 렌더링

    - 함수형 컴포넌트가 렌더링 => 컴포넌트 함수 호출 => 모든 내부 변수 초기화
👉🏻 useMemo 호출:
    - useMemo 렌더링 => 함수 호출 => memoize된 함수를 재사용

 

📢 Todo List 만들기! 

👉🏻 지금까지 배운 것들 활용해서 todoList 만들기

👉🏻 input창에 button 생성

👉🏻 input 값을 입력하고 버튼을 클릭하면 아이템 추가

👉🏻 삭제버튼 구현

👉🏻 각 아이템 클릭시, 취소선(다시 클릭시 사라짐(toggle))

 

todolist-app이라는 새로운 앱 생성하여 진행

 

app.js

import './App.css';
import TodoList from './components/TodoList';
import 'bootstrap/dist/css/bootstrap.min.css';

function App() {
  return (
    <div className="App">
      <TodoList />
    </div>
  );
}

export default App;

 

 

 

components/style.css

  .todoList {
    max-width: 600px;
    margin: 30px auto;
    padding: 20px;
    background-color: #f4f4f4;
    border-radius: 8px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  }
  
  .todoList-items {
    margin-top: 20px;
  }
  
  .todoCreate {
    display: flex;
    gap: 10px;
    margin-bottom: 20px;
  }
  
  .todoCreate input {
    flex: 1;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
    outline: none;
  }
  
  .todoCreate input:focus {
    border-color: #007bff;
    box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
  }
  
  .todoCreate button {
    padding: 10px 20px;
    background-color: #28a745;
    color: #fff;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.2s ease;
  }
  
  .todoCreate button:hover {
    background-color: #218838;
  }
  
  .todoItem {
    padding: 10px;
    background-color: #fff;
    border: 1px solid #ddd;
    border-radius: 4px;
    margin-bottom: 10px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  }

  .todoItem h3{
    width: 90%;
    margin: 0 auto;
    justify-content: space-between;
    display: flex;
    align-items: center;
    text-align: left;
  }
  
  .todoItem-text {
    cursor: pointer;
    flex: 1;
    text-decoration: none;
  }
  
  .todoItem-text.completed {
    text-decoration: line-through;
    color: #888;
  }
  
  .remove-btn {
    background-color: #dc3575;
    color: #fff;
    border: none;
    border-radius: 4px;
    padding: 5px 10px;
    cursor: pointer;
    transition: background-color 0.2s ease;
  }
  
  .remove-btn:hover {
    background-color: #91234d;
  }
  
  .title {
    margin: 0 0 20px 0;
    font-weight: 700;
    font-size: 100px;
    font-family: 'Poppins', sans-serif;
    color: #333;
    text-align: center;
    text-transform: uppercase;
    letter-spacing: 2px;
    background: linear-gradient(90deg, #28a745, #dc3575);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
  }

 

 

 

components/Todo.jsx

import React from 'react';

const Todo = ({ todo, onRemove, onToggle }) => {
    return (
        <div className='todoItem'>
            <h3>
                <span 
                    className={`todoItem-text ${!todo.active ? 'completed' : ''}`}
                    onClick={()=> onToggle(todo.id)} 
                >
                    {todo.todo}
                </span>
                {/* function으로 매개변수를 전달할 경우 */}
                <button 
                    className='remove-btn'
                    onClick={()=>onRemove(todo.id)}
                >
                    del
                </button>
            </h3>
        </div>
    );
};

export default Todo;

 

 

 

components/TodoCreate.jsx

import React from 'react';

const TodoCreate = ({ todo, onChange, onCreate }) => {

    return (
        <div className='todoCreate'>
            <input 
                type="text" 
                name='todo'  
                onChange={onChange}
                value={todo}
            />
            <button onClick={onCreate}>add</button>
        </div>
    );
};

export default TodoCreate;

 

 

 

components/TodoList.jsx

import React, { useState, useRef } from 'react';
import TodoCreate from './TodoCreate';
import Todo from './Todo';
import './style.css';


const TodoList = () => {
    const nextId = useRef(0);
    
    const [todos, setTodos] = useState([]);
    const [inputs, setInputs] = useState('');

    const todo = inputs;

    const onChange = (e) =>{
        setInputs(e.target.value);
    }

    const onCreate = ()=>{
        const todo = {
            id: nextId.current,
            todo: inputs,
            active: true
        };

        //현재 users에 user 추가 => concat
        setTodos(todos.concat(todo));

        // 기존 inputs 창 초기화
        setInputs('');

        nextId.current += 1 ;  //ref()  : 재 렌더링이 일어나지 않음.
    }

    const onRemove=(id) =>{
        setTodos(todos.filter(t=>t.id !== id))
    }

    const onToggle=(id)=>{
        setTodos(todos.map(t=>
            t.id === id ? {...t, active: !t.active} : t
        ))
    }

    return (
        <div className='todoList'>
            <h1 className='title'>Todo List</h1>
            <TodoCreate todo={todo} onChange={onChange} onCreate={onCreate} />
                <div className='todoList-items'>
                {todos.map(t=>(
                    <Todo todo={t} key={t.id} onRemove={onRemove} onToggle={onToggle} />
                ))}
            </div>
        </div>
    );
};

export default TodoList;

 

 

 

결과