[GDSC] 5주차 수업 보강 자료

현용찬·2024년 10월 7일
0

렌더링 뷰

코드 구조

코드 설명

App.js

import { useCallback, useRef, useState } from 'react';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
import TodoTemplate from './components/TodoTemplate';

function App() {
  // 기본적으로 렌더링할때 보여줄 데이터
  const [todos, setTodos] = useState([
    {
      id: 1,
      text: '리액트의 기초 알아보기',
      checked: true,
    },
    {
      id: 2,
      text: '컴포넌트 스타일링해 보기',
      checked: true,
    },
    {
      id: 3,
      text: '일정 관리 앱 만들어 보기',
      checked: false,
    },
  ]);
  // todos에 id가 3까지 저장되있으니 다음 list 추가하면 id값이 4부터 추가되게함
  const nextId = useRef(4);

  //props로 전달해야 할 함수를 만들때는 usecallback으로 함수를 감싸는걸 습관화 하기
  // 내용을 입력했을떄(text) 해당 내용을 id,text,checked가 안된 상태로 전달
  //todos.concat(todo)는 기존의 todos 배열을 변경하지 않고, todo 객체를 마지막에 추가한 새로운 배열을 반환한다.

  const onInsert = useCallback(
    (text) => {
      const todo = {
        id: nextId.current,
        text,
        checked: false,
      };
      setTodos(todos.concat(todo));
      nextId.current += 1;
    },
    [todos],
  );

  // filter 함수를 통해 리스트를 지우는 기능
  const onRemove = useCallback(
    (id) => {
      setTodos(todos.filter((todo) => todo.id !== id));
    },
    [todos],
  );

  // todos에 저장되있는 배열들을 map 함수를 통해 새로운 배열을 만듬

  const onToggle = useCallback(
    (id) => {
      setTodos(
        todos.map((todo) =>
          todo.id === id ? { ...todo, checked: !todo.checked } : todo,
        ),
      );
    },
    [todos],
  );
  //불변성을 유지하면서 특정 배열 원소를 업데이트 할 떄 다음과 같이 map함수를 사용하면 좋음

  return (
    <TodoTemplate>
      <TodoInsert onInsert={onInsert} />
      <TodoList todos={todos} onRemove={onRemove} onToggle={onToggle} />
    </TodoTemplate>
  );
}

export default App;

onInsert



  const onInsert = useCallback(
    (text) => {
      const todo = {
        id: nextId.current,
        text,
        checked: false,
      };
      setTodos(todos.concat(todo));
      nextId.current += 1;
    },
    [todos],
  );

내용을 입력했을떄(text) 해당 내용을 id,text,checked가 안된 상태로 전달.
todos.concat(todo)는 기존의 todos 배열을 변경하지 않고, todo 객체를 마지막에 추가한 새로운 배열을 반환한다.

onRemove


 const onRemove = useCallback(
   (id) => {
     setTodos(todos.filter((todo) => todo.id !== id));
   },
   [todos],
 );

filter 함수를 통해 리스트를 지우는 기능

onToggle



 const onToggle = useCallback(
   (id) => {
     setTodos(
       todos.map((todo) =>
         todo.id === id ? { ...todo, checked: !todo.checked } : todo,
       ),
     );
   },
   [todos],
 );

todos에 저장되있는 배열들을 map 함수를 통해 새로운 배열을 만듬

TodoTemplate.js

import React from 'react';
import './TodoTemplate.scss';

const TodoTemplate = ({ children }) => {
  return (
    <div className="TodoTemplate">
      <div className="app-title">일정 관리</div>
      {/* chidren을 통해 TodoInsert,TodoList 컴포넌트를 띄움 */}
      <div className="content">{children}</div>
    </div>
  );
};

export default TodoTemplate;

TodoInsert.js

import React, { useCallback, useState } from 'react';
import { MdAdd } from 'react-icons/md';
import './TodoInsert.scss';

//App.js에서 onInsert를 props로 받아옴
const TodoInsert = ({ onInsert }) => {
  const [value, setValue] = useState('');

  //input에서 내용을 입력하면 useState를 통해 value에 저장됨
  const onChange = useCallback((e) => {
    setValue(e.target.value);
  }, []);

  //onClick말고 onSubmit사용 이유는 엔터를 눌러도작동하기 떄문
  //onSubmit는 폼 제출 이벤트로 주로 form이랑 같이 쓰임
  //onClick은 클릭 이벤트로 주로 button,a 태그랑 같이 쓰임
  const onSubmit = useCallback(
    (e) => {
      onInsert(value); //App.js에 있는 onInsert 컴포넌트
      setValue(''); // 내용을 입력하고 해당 칸 부분을 초기화 시킴

      e.preventDefault(); //폼을 제출할 때 페이지가 새로고침되면 입력한 데이터가 초기화되거나, 사용자가 보던 상태가 사라질 수 있다. 이걸 방지
    },
    [onInsert, value],
  );

  return (
    <form className="TodoInsert" onSubmit={onSubmit}>
      <input
        placeholder="할말을 입력하세요"
        value={value}
        onChange={onChange}
      />
      <button type="submit">
        <MdAdd />
      </button>
    </form>
  );
};

export default TodoInsert;

onChange

  
  const onChange = useCallback((e) => {
    setValue(e.target.value);
  }, []);

input에서 내용을 입력하면 useState를 통해 value에 저장됨

onSubmit


  const onSubmit = useCallback(
    (e) => {
      onInsert(value); //App.js에 있는 onInsert 컴포넌트
      setValue(''); // 내용을 입력하고 해당 칸 부분을 초기화 시킴

      e.preventDefault(); //폼을 제출할 때 페이지가 새로고침되면 입력한 데이터가 초기화되거나, 사용자가 보던 상태가 사라질 수 있다. 이걸 방지
    },
    [onInsert, value],
  );

onClick말고 onSubmit 사용 이유는 엔터를 눌러도작동하기 떄문

onSubmit는 폼 제출 이벤트로 주로 form이랑 같이 쓰임
onClick은 클릭 이벤트로 주로 button,a 태그랑 같이 쓰임

TodoList.js

import React from 'react';
import TodoListitem from './TodoListitem';
import './TodoList.scss';

//App.js에서 todos, onRemove, onToggle 컴포넌트를 props로 받음
const TodoList = ({ todos, onRemove, onToggle }) => {
  return (
    <div className="TodoList">
      {/* todos를 todo로 새로운 배열을 만들어 넘긴다고 생각하면 됌 */}
      {todos.map((todo) => (
        // TodoListitem 컴포넌트로 아래 내용을 props로 넘김
        <TodoListitem
          todo={todo}
          key={todo.id}
          onRemove={onRemove}
          onToggle={onToggle}
        />
        //map을 사용하여 컴포넌트로 변환할 때는 key props를 전달해 주어야함
        //여기서 사용되는 key값은 각 항목마다 가지고 있는 고윳값인 id를 넣어주고 todo 데이터는 통째로 props로 전달
      ))}
    </div>
  );
};

export default TodoList;

map

 
      {todos.map((todo) => (
        // TodoListitem 컴포넌트로 아래 내용을 props로 넘김
        <TodoListitem
          todo={todo}
          key={todo.id}
          onRemove={onRemove}
          onToggle={onToggle}
        />
        
        
      ))}

todos를 todo로 새로운 배열을 만들어 넘긴다고 생각하면 됌
map을 사용하여 컴포넌트로 변환할 때는 key props를 전달해 주어야함
여기서 사용되는 key값은 각 항목마다 가지고 있는 고윳값인 id를 넣어주고 todo 데이터는 통째로 props로 전달

TodoListitem.js

import React from 'react';
import {
  MdCheckBoxOutlineBlank,
  MdCheckBox,
  MdRemoveCircleOutline,
} from 'react-icons/md';
import cn from 'classnames';
import './TodoListitem.scss';

// TodoInsert.js에서 todo, onRemove, onToggle를 props로 받음
const TodoListitem = ({ todo, onRemove, onToggle }) => {
  // todo 객체에서 속성 추출해서 <div className="text">{text}</div>와 같이 렌더링하는데 필요
  const { id, text, checked } = todo;

  return (
    <div className="TodoListItem">
      <div className={cn('checkbox', { checked })} onClick={() => onToggle(id)}>
        {/* 조건에 따라 css 이름을 변경하도록 cn 사용 */}

        {/* 즉 기본 className은 checkbox이고 { checked }는 checked가 true일 때만 checked라는 클래스가 추가로 적용 */}
        {checked ? <MdCheckBox /> : <MdCheckBoxOutlineBlank />}

        <div className="text">{text}</div>
      </div>
      <div className="remove" onClick={() => onRemove(id)}>
        {/*onRemove는 App.js에서 선언한 컴포넌트  */}
        <MdRemoveCircleOutline />
      </div>
    </div>
  );
};

export default TodoListitem;

todo

  
  const { id, text, checked } = todo;

todo 객체에서 속성 추출해서

<div className="text">{text}</div>

와 같이 렌더링하는데 필요

cn

    <div className={cn('checkbox', { checked })} onClick={() => onToggle(id)}>
      

조건에 따라 css 이름을 변경하도록 cn 사용
즉, 기본 className은 checkbox이고 { checked }는 checked가 true일 때만 checked라는 클래스가 추가로 적용

profile
항상 웃어 봅시다

0개의 댓글