React | 컴포넌트 성능 최적화 02 방법1 (useState / React.memo / 함수 재생성 제어 / 관련 컴포넌트)

Kate Jung·2021년 11월 7일
0

React

목록 보기
13/28
post-thumbnail

🔶 useState의 기본 값 형태에 따른 차이

🔹 코드

(생략)

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

const App = () => {
  const [todos, setTodos] = useState(createBulkTodo);

	(생략)
};

export default App;

🔹 주의 | useState의 기본 값 → 함수 넣어 줌

  • useState(createBulkTodo()) 의 경우

    → 리렌더링될 때마다 createBulkTodo 함수 호출

  • useState(createBulkTodo)의 경우

    == 파라미터: 함수 형태

    → 컴포넌트가 처음 렌더링될 때만 createBulkTodo 함수 실행

🔶 React.memo 활용

🔹 컴포넌트 리렌더링 방지

  • 클래스형 컴포넌트 → shouldComponentUpdate(라이프사이클) 사용
  • 함수형 컴포넌트 → React.memo 사용

🔹 React.memo

컴포넌트 props 변화x → 리렌더링x

  • 사용법

    컴포넌트 감싸기

    (생략)
    
    export default React.memo(컴포넌트명);

🔶 함수 계속 만들어지는 상황 방지 방법

🔹 방지 이유

  • 의존성 배열 내부의 state 바뀔 때마다 → 함수(첫 번째 파라미터) 계속 재생성

🔹 useState의 함수형 업데이트

◼ 함수형 업데이트란?

setState의 파라미터에 업데이트 함수를 넣는 것

  • 업데이트 함수란?

    상태 업데이트를 어떻게 할지 정의

◼ 함수형 업데이트 예시

  • 기존 코드
    const [number, setNumber] = useState(0);
    const onIncrease = useCallback(
    	() => setNumber( number + 1 ),
    	[number],
    )
  • 적용 코드
    const [number, setNumber] = useState(0);
    // prevNumbers === 현재 number 값
    const onIncrease = useCallback(
    	() => setNumber(prevNumber => prevNumbers + 1),
    	[],
    )
  • 참고

    prevNumber => 추가한 것과 배열([]) 만 변화

🔹 useReducer

◼ 예시 코드

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

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

function todoReducer(todos, action) {
  switch (action.type) {
    case 'INSERT': // 추가
      // { type: 'INSERT', todo: { id: 2501, text: '할일 2501', checked: false } }
      return todos.concat(action.todo);
    case 'REMOVE': // 삭제
      // { type: 'REMOVE', id: 1 }
      return todos.filter((todo) => todo.id !== action.id);
    case 'TOGGLE': // 토글
      // { type: 'TOGGLE', id: 1 }
      return todos.map((todo) =>
        todo.id === action.id ? { ...todo, checked: !todo.checked } : todo,
      );
    default:
      return todos;
  }
}

const App = () => {
  const [todos, dispatch] = useReducer(todoReducer, undefined, createBulkTodos);

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

  const onInsert = useCallback((text) => {
    const todo = {
      id: nextId.current,
      text,
      checked: false,
    };
    dispatch({ type: 'INSERT', todo });
    nextId.current += 1; // nextId 1 씩 더하기
  }, []);

  const onRemove = useCallback((id) => {
    dispatch({ type: 'REMOVE', id });
  }, []);

  const onToggle = useCallback((id) => {
    dispatch({ type: 'TOGGLE', id });
  }, []);

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

export default App;

◼ 파라미터의 변화

  • 기존 useReducer 두 번째 파라미터 = 초기 상태

  • 두 번째 파라미터 = undefined
  • 세 번째 파라미터 = 초기 상태 제작 함수(createBulkTodos)

◼ 효과

(컴포넌트가) 맨 처음 렌더링될 때만 함수(createBulkTodos) 호출

◼ 장단점

  • 단점

    기존 코드 많이 고쳐야 함

  • 장점

    상태 업데이트 로직들 → 컴포넌트 바깥에 둘 수 있음.

🔹 두 가지 방법의 성능 차이: 비슷

→ 취향에 따라 선택

🔶 관련 컴포넌트의 최적화

관련 컴포넌트 → 같이 최적화

  • 예시

    '리스트' 관련 컴포넌트 최적화할 때

    → (리스트 내부 + 리스트 자체) 컴포넌트 모두 최적화 해야 함.

  • 이유

    미리 최적화 시키는 것 (추후 발생 가능한 불필요한 렌더링 대비)

    • 당장 불필요한 렌더링이 발생 x 해도

      추후 관련된 타 컴포넌트에서 변화가 생겨 불필요한 렌더링 생길 가능성 有

      → 미리 대비/성능 최적화(React.memo활용)

profile
복습 목적 블로그 입니다.

0개의 댓글