[한 입 크기로 잘라 먹는 리액트] 리액트(React) 03

hidihyeonee·2025년 2월 4일
0

2025.02.04 작성

OS : Window
개발환경 : VScode
개발언어 : JavaScript
프레임워크 : React


[할 일 관리] 앱 만들기

중요한 개념 정리

이벤트 핸들러

submit (입력값 제출)

const submit = () => {
    if (!content) { // 입력하지 않으면
        inputRef.current.focus(); // input 창에 포커스 이동
        return;
    }
    onCreate(content); // 새로운 Todo 추가
    setContent(""); // 입력 필드 초기화
};
  • 입력값이 없으면 input에 포커스를 줌.
  • onCreate(content)를 실행하여 부모 컴포넌트에서 Todo 추가.
  • setContent("")를 호출해 입력 필드 초기화.

onKeyDown (Enter 키 입력 감지)

const onKeyDown = (e) => {
    if (e.keyCode === 13) {
        submit();
    }
};
  • Enter (keyCode === 13) 입력 시 submit() 실행.

TodoList (검색 기능 추가)

TodoList.js

import TodoItem from "./TodoItem";
import "./TodoList.css";
import { useState } from "react";

const TodoList = ({ todo }) => {
    const [search, serSerch] = useState('');
    const onChangeSearch = (e) => {
        serSerch(e.target.value);
    };

    const getSearchResult = () => {
        return search === "" 
        ? todo 
        : todo.filter(it => it.content.includes(search))
    };

    return (
        <div className="TodoList">
            <h4>Todo List🌱</h4>
            <input
                className="searchbar"
                placeholder="검색어를 입력하세요"
                value={search}
                onChange={onChangeSearch}
            />
            <div className="list_wrapper">
                {getSearchResult().map((a) => (
                    // <div key={a.id}>{a.content}</div>
                    <TodoItem key={a.id} {...a} />
                ))}
            </div>
        </div>
    );
};

export default TodoList;

검색 결과 필터링 (getSearchResult)

const getSearchResult = () => {
    return search === "" 
        ? todo 
        : todo.filter(it => it.content.includes(search));
};
  • 검색어가 비어 있으면 전체 목록 반환: search === "" ? todo
  • 검색어가 입력되면 필터링: todo.filter(it => it.content.includes(search))
    - includes(search): content 값이 검색어를 포함하면 해당 항목 유지.
<div className="list_wrapper">
        {getSearchResult().map((a) => (
            <TodoItem key={a.id} {...a} />
        ))}
</div>
  • 검색된 결과만 렌더링 > getSearchResult().map(...)을 사용해 필터링된 리스트만 출력.

TodoList (체크박스 기능)

체크박스 클릭 (onUpdate) → App.js에서 상태 업데이트

const onUpdate = targetId => {
    setTodo(
        todo.map(it => {
            if (it.id == targetId) {
                return { ...it, isDone: !it.isDone }; // isDone 값을 반대로 변경
            } else {
                return it;
            }
        })
    );
};
  • map()을 이용해 id가 일치하는 항목을 찾아 isDone을 true <-> false 토글.
    ✔️ 불변성을 유지하기 위해 ...it을 사용하여 기존 데이터를 복사한 후 변경.

TodoList (삭제 기능)

삭제 버튼 클릭 (onDelete) → App.js에서 상태 업데이트

const onDelete = (targetId) => {
    setTodo(todo.filter((it) => it.id !== targetId));
};
  • filter()를 사용하여 id가 일치하지 않는 항목만 남김 → 선택한 항목 삭제
    - filter()는 기존 배열을 변경하지 않고, 조건을 만족하는 요소만 새로운 배열로 반환.
    - 즉, id가 targetId와 일치하지 않는 항목만 남김.
  • setTodo()로 업데이트하여 화면에서 삭제된 것처럼 보임.

[할 일 관리] 앱 업그레이드

useState를 useReducer로 바꾸기

import './App.css';
import Header from './component/Header';
import TodoEditor from './component/TodoEditor';
import TodoList from './component/TodoList';
import { useRef, useReducer } from 'react';
import TestComp from './component/TestComp';

const mockTodo = [
  {
    id: 0,
    isDone: false,
    content: "React 공부하기",
    createDate: new Date().getTime(),
  },
  {
    id: 1,
    isDone: false,
    content: "빨래 널기기",
    createDate: new Date().getTime(),
  },
  {
    id: 2,
    isDone: false,
    content: "노래 연습하하기",
    createDate: new Date().getTime(),
  },
];

function reducer(state, action) {
  switch (action.type) {
    case "CREATE": {
      return [action.newItem, ...state]; // 새로운 객체가 앞에 들어오는 형태로 배열을 다시 만들어서 return
    }
    case "UPDATE": {
      return state.map((it) =>
        it.id === action.targetId
          ? {
            ...it,
            isDone: !it.isDone,
          }
          : it
      );
    }
    case "DELETE": {
      return state.filter((it) => it.id !== action.targetId);
    }
    default:
      return state;
  }
}

function App() {
  const idRef = useRef(3);

  const [todo, dispatch] = useReducer(reducer, mockTodo);

  const onCreate = (content) => {
    dispatch({
      type: "CREATE",
      newItem: {
        id: idRef.current,
        content,
        isDone: false,
        createDate: new Date().getTime(),
      },
    });
    idRef.current += 1;
  };

  const onUpdate = (targetId) => {
    dispatch({
      type: "UPDATE",
      targetId,
    });
  };

  const onDelete = (targetId) => {
    dispatch({
      type: "DELETE",
      targetId,
    });
  };

  return (
    <div className="App">
      <TestComp />
      <Header />
      <TodoEditor onCreate={onCreate} />
      <TodoList todo={todo} onUpdate={onUpdate} onDelete={onDelete} />
    </div>
  );
}

export default App;
  • useReducer는 상태 관리 함수(reducer)와 초기 상태(mockTodo)를 받아 상태를 관리
  • dispatch(action)을 호출하면 reducer(state, action)이 실행됨
  • state는 현재 todo 리스트, action은 수행할 작업을 나타냄 (CREATE, UPDATE, DELETE)

1. ✅ 할 일 추가 (onCreate)

const onCreate = (content) => {
  dispatch({
    type: "CREATE",
    newItem: {
      id: idRef.current,
      content,
      isDone: false,
      createDate: new Date().getTime(),
    },
  });
  idRef.current += 1;
};

2. ✅ 할 일 완료 상태 변경 (onUpdate)

const onUpdate = (targetId) => {
  dispatch({
    type: "UPDATE",
    targetId,
  });
};

3. ✅ 할 일 삭제 (onDelete)

const onDelete = (targetId) => {
  dispatch({
    type: "DELETE",
    targetId,
  });
};

useCallback 개념 정리

useCallback은 React의 Hook 중 하나로, 특정 함수를 메모이제이션(memoization)하여 불필요한 재생성을 방지하는 역할을 합니다.


멍청멍청 실수🛠️

1. 오타 조심~


2. { } 안에 들어갈 변수명도 오타나면 오류 남~

profile
벨로그 쫌 재밌네?

0개의 댓글