[리액트를 다루는 기술] React.memo와 함수형 setState를 이용해 의존성은 없애고 성능은 향상시키기

쿼카쿼카·2022년 9월 8일
0

개발자 도구로 성능체크

  • 2500개의 데이터인 todos에 변화가 생기면 전체가 리렌더링 되어 성능저하
  • '할 일 0' 체크하는데 한국인이라면 못 참을 렉이 걸림
  • 렌더링 시간도 50m는 뛰어갈 수 있는 328.7ms나 걸림
  • 그 이유는 '할 일 0'만 체크했는데 전체가 리렌더링 되기 때문. 의리 미쳤네
  • 서로 영향을 주지 않기 위해 아래 코드로 수정

App.js

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

function createBulkTodos() {
  const array = [];
  for(let i=0; i <= 2500; i++) {
    array.push({
      id: i,
      text: `할 일 ${i}`,
      checked: false,
    });
  }
  return array;
}

function App() {
  const [todos, setTodos] = useState(createBulkTodos);

  // 고윳값으로 사용될 id
  // ref를 사용하여 변수 담기
  const nextId = useRef(2501);

  // props로 전달되는 함수는 useCallback으로 전달
  const onInsert = useCallback(text => {
    const todo = {
      id: nextId.current,
      text,
      checked: false,
    };
    setTodos(todos => todos.concat(todo));
    nextId.current++;
  }, []); // setTodos를 함수형으로 만들고 deps를 비움

  // remove 함수
  const onRemove = useCallback(id => {
    setTodos(todos => todos.filter(todo => todo.id !== id))
  }, []); // setTodos를 함수형으로 만들고 deps를 비움

  // checked: true 변경 함수
  const onToggle = useCallback(id => {
    setTodos(todos => todos.map(todo => 
      todo.id === id ? {...todo, checked: true} : todo
    ))
  }, []); // setTodos를 함수형으로 만들고 deps를 비움

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

export default App;

TodoListItem.js

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

function TodoListItem({todo, onRemove, onToggle}) {
  const {id, text, checked} = todo;
  return (
    <div className='TodoListItem'>
      <div className={cn('checkbox', {checked})} onClick={() => onToggle(id)}>
        {checked ? <MdCheckBox /> : <MdCheckBoxOutlineBlank />}
        <div className='text'>{text}</div>
      </div>
      <div className="remove" onClick={() => onRemove(id)}>
        <MdRemoveCircleOutline />
      </div>
    </div>
  )
}

export default React.memo(TodoListItem); // React.memo로 감싸기

React.memo()

  • 컴포넌트 props가 바뀌지 않는다면 리렌더링 안 일어남
  • React.memo(TodoListItem)처럼 원하는 컴포넌트 감싸주기

함수형 setState

  • 하수들이 setState에 바로 원하는 값을 넣어줬다면 고수들은 함수형으로 넣어줌
  • setState(prevValue => prevValue +1)처럼 매개변수로 업데이트 이전 값을 받아 상태변경 알려줌
  • App.js에서 setTodos(todos => todos.filter(todo => todo.id !== id)), []);
    • setTodos속 함수 매개변수 todos는 변수 todos가 아닌 업데이트 이전 값 todos
    • 따라서 deps에 todos를 넣어줄 필요 없음

결과

  • 렌더링 시간 줄어든 거 보소~~~
  • 밑 그래프에 회식 빗금은 렌더링 되지 않은 아이들이다. 의리박살
profile
쿼카에요

0개의 댓글