렌더링 최적화를 React Hook

15380 단어 ReactReact

크롬의 확장도구 React Developer Tools 사용하기

React Developer Toolscomponents 에서 highlight updates when components render항목을 check하면 렌더링이 되는 영역을 눈으로 확인 할 수 있습니다.

리액트에서 성능 최적화란?

React는 먼저 컴퍼넌트를 렌더링(rendering) 한 뒤, 이전 렌더된 결과와 비교하여 DOM 업데이트를 결정합니다. 만약 렌더 결과가 이전과 다르다면, React는 DOM을 업데이트합니다.

Re-Rendering이 되는 경우에는 크게 3가지가 존재합니다.

  • 전달받은 props 값이 업데이트 되었을 때
  • state가 변경되었을 때
  • 부모 컴포넌트가 re-render 되었을 때

여러개의 state 와 props 중에 하나만 변경되어도 계속 re-render가 일어나게 되고, 부모 컴포넌트의 re-ender로 인해 자식 컴포넌트까지 불필요한 re-rendering이 일어나게 된다면 성능 저하문제를 유발할 수 있습니다.

React에서 성능 최적화란 Re-Rendering을 최소화 하는 것입니다.

  • Memoization이해하기 🔻 이미 계산 된 연산 결과를 기억 해 두었다가 동일한 계산을 시키면, 다시 연산하지 않고 기억 해 두었던 데이터를 반환 시키게 하는 방법.

useCallback()를 사용한 성능 최적화

import React, { useCallback } from "react";

useCallback(() => {}, []);

//첫번째 인자 = 콜백함수
//두번쨰 인자 = 의존성배열 

React공식문서

메모이제이션된 콜백을 반환합니다.인라인 콜백과 그것의 의존성 값의 배열을 전달하세요. useCallback은 콜백의 메모이제이션된 버전을 반환할 것입니다. 그 메모이제이션된 버전은 콜백의 의존성이 변경되었을 때에만 변경됩니다. 이것은, 불필요한 렌더링을 방지하기 위해 (예로 shouldComponentUpdate를 사용하여) 참조의 동일성에 의존적인 최적화된 자식 컴포넌트에 콜백을 전달할 때 유용합니다

참조의 동일성에 의존적인 최적화된 자식 컴포넌트에 콜백을 전달할 때 유용하다는것은 다시말해 최적화된 자식 컴포넌트에 props로 콜백 함수를 내려줄 때를 말하는것 같습니다.

  • 예제 코드
    
    const TodoTemplate = () => {
      const [localTodo, setLocalTodo] = useLocalStorage<ITodo[]>('todoList', []);
    
      const deleteCompletedTodo = useCallback((): void => {
    
      const todoUpDate = useCallback((todo: ITodo): void => {
        setLocalTodo((prevState) => [...prevState, todo]);
      }, []);
    
      return (
        <>
          <TodoCreate todoUpDate={todoUpDate} />
          <TodoList localTodo={localTodo} setLocalTodo={setLocalTodo} />
        </>
      );
    };
    TodoTemplate에서 todoUpDate라는 함수를 자식 컴포넌트인 TodoCreate에 전달하였고 TodoList에서는 바뀐 localTodo를 사용하여 화면을 그려냅니다. 만약에 useCallback 사용하지 않으면 localTodo값이 TodoList안에서 바뀌게 될 때마다 전혀 상관없는 todoUpDate가 재생성 됩니다. 그것을 막기위해 위와같이 의존성 배열에 빈 배열을 주어 초기에 한번만 함수를 생성되도록 하였습니다.

useMemo()를 사용한 성능 최적화

import React, { useMemo } from "react";

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

React공식문서

“생성(create)” 함수와 그것의 의존성 값의 배열을 전달하세요.useMemo는 의존성이 변경되었을 때에만 메모이제이션된 값만 다시 계산 할 것입니다. 이 최적화는 모든 렌더링 시의 고비용 계산을 방지하게 해 줍니다.useMemo로 전달된 함수는 렌더링 중에 실행된다는 것을 기억하세요. 통상적으로 렌더링 중에는 하지 않는 것을 이 함수 내에서 하지 마세요. 예를 들어, 사이드 이펙트(side effects)는 useEffect에서 하는 일이지 useMemo에서 하는 일이 아닙니다.배열이 없는 경우 매 렌더링 때마다 새 값을 계산하게 될 것입니다.

useMemo는 값을 반환합니다!

어떤 함수가 있고 그 함수가 어떤 값을 리턴하고있는데 그 리턴까지의 연산을 최적화하고싶다면 useMemo를 사용해서 댑스에 어떤값이 변화할때만 그 리턴까지의 연산을 수행할것인지를 명시하면 그 함수를 값처럼 사용하여 연산최적화를 할 수 있습니다.

  • 예제 코드
    export function Movie({ title, releaseDate }) {
      return (
        <div>
          <div>Movie title: {title}</div>
          <div>Release date: {releaseDate}</div>
        </div>
      );
    }
    
    export default React.memo(Movie);
    
    //사용시
    
    <Movie
      movieTitle="Heat"
      releaseDate="December 15, 1995"
    />
    
    // 다시 렌더링 할 때 React는 MemoizedMovie 함수를 호출하지 않는다.(props로 넘겨주는 값이 변하지 않았을 경우!)
    // 리렌더링을 막는다.
    <Movie
      movieTitle="Heat"
      releaseDate="December 15, 1995"
    />
    메모이징 한 결과를 재사용 함으로써, React에서 리렌더링을 할 때 가상 DOM에서 달라진 부분을 확인하지 않아 성능상의 이점을 누릴 수 있다.

props 동등 비교 커스터마이징

React.memo()는 props 혹은 props의 객체를 비교할 때 얕은(shallow) 비교를 합니다.

비교 방식을 수정하고 싶다면 React.memo() 두 번째 매개변수로 비교함수를 만들어 넘겨주면 됩니다.

React.memo(Component, [areEqual(prevProps, nextProps)]);

areEqual(prevProps, nextProps) 함수는 prevProps와 nextProps가 같다면 true를 반환합니다.

위에 예제 코드의 Movie의 porps가 동일한지 수동으로 비교해 보겠습니다.

function moviePropsAreEqual(prevMovie, nextMovie) {
  return (
    prevMovie.title === nextMovie.title &&
    prevMovie.releaseDate === nextMovie.releaseDate
  );
}

const MemoizedMovie = React.memo(Movie, moviePropsAreEqual);

moviePropsAreEqual() 함수는 이전 props와 현재 props가 같다면 true를 반환하며 이전값을 그대로 사용하여 리렌더링이 발생하지 않습니다.

useCallback()과 useMemo()와 공통점

deps가 변하면 memoization된것을 반환합니다.

useCallback()과 useMemo()와 차이점

useMemo는 함수의 값을 반환,함수의 연산량이 많을때 이전 결과값을 재사용하는 목적이고,useCallback은 함수를 반환,함수가 재생성 되는것을 방지하기 위한 목적입니다.

좋은 웹페이지 즐겨찾기