AWS BACK DAY 59. "React의 Component와 전개 연산자 활용, TodoList 구현하기"

이강용·2023년 3월 27일
1

React

목록 보기
3/4

React를 쓰는 이유?

  1. 선언적 프로그래밍 : React는 선언적 프로그래밍을 지원하여 개발자가 상호 작용하는 UI요소를 간단하게 정의할 수 있음
  2. 가상 DOM(Document Object Model): React는 가상 DOM을 사용하여 빠른 UI 업데이트를 가능하게 함. 가상 DOM은 React가 변경된 부분만 실제 DOM에 적용하여 불필요한 렌더링을 방지
  3. 컴포넌트 기반: React는 컴포넌트 기반 아키텍처를 사용하여 코드의 재사용성을 높이고 유지 보수를 쉽게 함
  4. 유연성: React는 라이브러리이므로 다른 라이브러리나 프레임워크와 함께 사용 가능
  5. 대규모 애플리케이션 구축 : React는 대규모 애플리케이션에서도 효과적으로 작동함

Component는 함수(Function)다?

  • React에서는 컴포넌트는 함수 또는 클래스로 생성됨, 함수형 컴포넌트는 간단하게 함수로 구성되어 있으며, 클래스형 컴포넌트는 ES6 클래스로 생성되어 있음.

index.js > App.js > > InputSample.js > UserList.js

얕은 복사 (Shallow Copy): 객체의 참조(reference)만을 복사. 즉, 원본 객체와 복사본 객체가 같은 객체를 참조하게됨, 따라서 원본 객체를 변경하면 복사본 객체도 영향을 받게 됨
const user = inputs → inputs객체를 변경하면 user객체도 변경
깊은 복사 (Deep Copy): 객체의 값(value)를 복사. 원본 객체와 복사본 객체가 다른 객체를 참조하게 됨, 따라서 원본 객체를 변경해도 복사본 객체는 영향을 받지 않음.

const user ={
	...inputs
}

위에서 제시한 코드에서는 객체 전개 연산자(...)를 사용하여 깊은 복사를 수행

React에서는 상태(state)를 변경할 때 얕은 복사와 깊은 복사를 구분하여 사용해야함
상태가 간단한 객체나 배열이면 얕은 복사를 사용해도 문제가 없지만, 중첩된 객체나 배열이 포함된 상태를 변경할 때는 깊은 복사를 사용해야 함. ❗️ 이를 사용하지 않으면 예기치 않은 오류가 발생할 수 도 있음

전개 연산자 ...

  • 배열에서 전개 연산자를 사용하면 배열 요소를 하나씩 꺼내서 전개함

  • 예를들어 [1,2,3]배열을 [...[1,2,],3]으로 펼치면 [1,2,3]이 됨

  • 이는 concat 메서드를 사용하는 것과 동일한 결과를 반환함

    객체에서 전개 연산자를 사용하면 객체의 프로퍼티(속성)을 하나씩 꺼내서 전개함

  • 전개 연산자는 불변성을 유지하면서 객체 또는 배열을 수정할 때 유용하게 사용됨

  • 객체나 배열의 내용을 새로운 배열이나 객체로 복사하고, 해당 새로운 객체나 배열에서 수정 작업을 수행하면 원본 객체나 배열에는 영행을 주지 않으면서 안전하게 작업을 수행할 수 있음

요구사항

TodoList 넣기

  • 왼쪽 사이드바
    -TodoList
    -(주소는 /todo)
  • input 창, + 버튼 넣기
  • list 만들고, 삭제 버튼 넣기

TodoList.js

/** @jsxImportSource @emotion/react */
import React, { useState, useEffect, useRef } from 'react';
import * as S from './style';

const TodoList = () => {
  const [todo, setTodo] = useState([]);
  const [inputValue, setInputValue] = useState('');
  const inputRef = useRef(null);

  useEffect(() => {
    inputRef.current.focus();
  }, []);

  const addTodo = () => {
    if (inputValue.trim() !== '') {
      setTodo([...todo, inputValue.trim()]);
      setInputValue('');
    }
  };

  const deleteTodo = (index) => {
    setTodo(todo.filter((_, i) => i !== index));
  };

  const handleKeyPress = (e) => {
    if (e.key === 'Enter') {
      addTodo();
    }
  };

  return (
    <div css={S.Container}>
      <h2>Todo List</h2>
      <div>
        <input
          type="text"
          value={inputValue}
          onChange={(e) => setInputValue(e.target.value)}
          onKeyPress={handleKeyPress}
          placeholder="Enter todo"
          ref={inputRef}
        />
        <button type="button" onClick={addTodo}>
          Add
        </button>
      </div>
      <ul>
        {todo.map((todo, index) => (
          <li css={S.TodoItem} key={index}>
            <div css={S.TodoText}>{todo}</div>
            <button css={S.DeleteButton} onClick={() => deleteTodo(index)}>
              Delete
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default TodoList;
  • React Hook를 이용하여 state를 설정, useState 함수를 사용하여 todoinputValue 라는 두 가지 상태를 설정
  • todo는 배열로서 사용자가 추가한 할 일 목록을 저장하며, inputValue는 사용자가 할 일을 추가할 때 입력한 값을 저장
  • useRef를 이용하여 inputRef 변수를 설정, 이 변수는 JSX의 input 요소와 연결됨
  • useEffect Hook을 이용하여 렌더링 후 input 요소에 focus를 설정
  • addTodo 함수는 사용자가 입력한 할 일을 todo 배열에 추가
  • 이 때, trim() 함수를 이용하여 입력한 문자열 앞 뒤 공백을 제거
  • deleteTodo 함수는 인덱스에 해당하는 할 일 항목을 배열에서 삭제
  • handleKeyPress 함수는 사용자가 Enter 키를 누르면 addTodo함수를 호출

-JSX를 이용하여 TodoList를 렌더링함

  • input 요소와 button 요소는 addTodo 함수와 inputValue 상태를 이용하여 할 일 항목을추가하는 기능을 구현
  • ulli 요소는 todo 배열을 이용하여 할 일 목록을 나타냄
  • 삭제 버튼은 deleteTodo 함수를 이용하여 해당 항목을 삭제
  • 이때, TodoItem,TodoText,DeleteButton 과 같은 스타일을 가진 요소들은 style.js 파일에 작성된 CSS 스타일을 이용

style.js

import { css } from '@emotion/react';

export const Container = css`
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 20px 20px 10px 20px;
`;

export const TodoItem = css`
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 300px;
  padding: 10px;
  margin-bottom: 10px;
  background-color: #f1f1f1;
  border-radius: 5px;
  box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.3);
`;

export const TodoText = css`
  flex: 1;
  margin-right: 10px;
`;

export const DeleteButton = css`
  background-color: #ff5c5c;
  color: white;
  border: none;
  border-radius: 5px;
  padding: 5px 10px;
  cursor: pointer;
`;

export const Input = css`
  margin-bottom: 10px;
`;

export const TodoList = css`
  margin-bottom: 10px;
`;

Todo

/** @jsxImportSource @emotion/react */
import { css } from "@emotion/react";
import { FcPlus } from 'react-icons/fc';
import { BiPen } from 'react-icons/bi';
import { TiTrash } from 'react-icons/ti';
import React, { useState,useRef } from 'react';



const TodoContainer = css`
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 100px;
  width: 100%;
`;

const TodoAddition = css`
  position: sticky;
  top: 0px;
  box-sizing: border-box;
  margin-bottom: 20px;
  border-radius: 7px;
  padding: 10px;
  width: 600px;
  height: 60px;

  background-color: #eee;
`;

const AdditionInput = css`
  box-sizing: border-box;
  border: none;
  outline: none;
  border-bottom: 1px solid white;
  padding: 0px  50px 0px 10px;
  width: 100%;
  height: 100%;
  font-size: 1.2rem;
  background-color: #eee;

`;

const TodoAddButton = css`
  position: absolute;
  transform: translateY(-50%);
  top: 50%;
  right: 15px;
  display: flex;
  justify-content: center;
  align-items: center;
  border: none;
  padding: 0;
  width: 35PX;
  height: 35px;
  font-size: 1.3rem;
  background-color: #ffffff00;
  transition: all 0.3s ease;
  cursor: pointer;
  &:hover {
    transform: translateY(-50%) rotate(180deg)  scale(1.5);
  }
`;

const TodoList = css`
  box-sizing: border-box;
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 5px;
  border-radius: 7px;
  padding: 10px;
  width: 600px;

  background-color: #fafafa;

`;

const TodoContent = css`
  width: 85%;
  height: 40px;
`;

const ItemGroup = css`
  display: flex;
  align-items: center;
  height: 40px;


`;
const ItemButton = css`
  display: flex;
  align-items: center;
  border: none;
  height: 100%;
  color: #999;
  background-color: #ffffff00;
  cursor: pointer;
  &:hover {
    color: #121212;
  }



`;

const Todo = () => {

  const[input, setInput] = useState({
    id:0,
    content:''

  });
  
  const [todoList, setTodoList] = useState([]);

  const todoId = useRef(1);

  const onChange = (e) => {
    setInput({
      ...input, 
      content: e.target.value
    });
  }

  const onKeyUp = (e) => {
    if(e.keyCode === 13) {
      onClick();
    }

  }

  const onClick = () => {
    const  todo ={
      ...input,
      id: todoId.current++
    }

    setTodoList([...todoList, todo]);
    setInput({...input,content: ''});
  }

  const onRemove = (id) => {
    setTodoList(todoList.filter(
      todo => {
        return todo.id !== id;
      }

    ))
  }


  return (
    <div css={TodoContainer}>
      <div css={TodoAddition}>
        <input css={AdditionInput} type="text" placeholder="Add your new Todo" onChange={onChange} onKeyUp={onKeyUp} value={input.content} />
        <button css={TodoAddButton} onClick={onClick}><FcPlus /></button>
      </div>
      {todoList.map(
          todo => {
            return(
              <div css={TodoList} key={todo.id}>
                <div css={TodoContent}>{todo.content}</div>
                <div css={ItemGroup}>
                    <button css={ItemButton}><BiPen /></button>
                    <button css={ItemButton} onClick={() => onRemove(todo.id)}><TiTrash /></button>
                </div>
              </div>
            );
          }
      )}
    </div>
  );
};

export default Todo;

profile
HW + SW = 1

0개의 댓글