• TOC {:toc}

이 글은 벨로퍼트와 함께하는 모던 React중 19. React.memo 를 사용한 컴포넌트 리렌더링 방지의 내용을 복습하기위해 핵심 내용을 요약 정리한 글입니다.

  • 제가 필요한 부분 위주로 확인하면서 정리하고 있어 글에 덜 작성된 부분이 있을 수 있습니다.
  • 글 작성 후 원문의 내용이 수정되거나 내용을 이해하기 위한 개인적인 설명이나 해석이 있을 수 있습니다. 되도록 원문을 참고해주시길 바랍니다.
  • 잘못된 부분이 있다면 댓글이나 그 외 편하신 방법으로 알려주시면 감사하겠습니다.

React.memo는, 컴포넌트의 props 가 바뀌지 않았다면, 리렌더링을 방지하여 컴포넌트의 리렌더링 성능 최적화를 해줄 수 있는 함수다.

  • 즉, 컴포넌트에서 리렌더링이 필요한 상황에서만 리렌더링을 하도록 설정해줄 수 있다.

React.memo를 사용하는 법은 간단하다. component를 React.memo로 감싸주면 된다.

/* CreateUser.js */

import React from 'react'; 

const CreateUser = ({ username, email, onChange, onCreate }) => {
  return ( /* ... */ );
};

export default React.memo(CreateUser); // export 할 때 React.memo로 컴포넌트를 감싸줌
/* UserList.js */

import React from 'react';

// 별도로 export하지 않는 컴포넌트는 선언할 때 감싸서 선언.
const User = React.memo(function User({ user, onRemove, onToggle }) {
  return ( /* ... */ );
});

function UserList({ users, onRemove, onToggle }) {
  return ( /* ... */ );
}

export default React.memo(UserList);

적용을 다 하고 input을 수정하면 onChange 함수가 호출되는데 이를 props로 갖지 않는 UserList는 리렌더링이 되지 않는것을 확인할 수 있다.

반면에, User 중 하나라도 수정되면 모든 User 들이 리렌더링되고, CreateUser도 리렌더링된다. 이는 users 배열이 바뀔때마다 이를 useCallback deps로 갖는 onCreate, onToggle, onRemove 모두 새로 만들어지기 때문이다.

const onCreate = useCallback(() => { /* ... */ }, [users, username, email]);

const onRemove = useCallback( /* ... */ }, [users]
);

이것까지 최적화하고 싶다면

  • deps 에서 users 를 지우고,
  • 함수들에서 현재 useState 로 관리하는 users 를 참조하지 않게 해야한다.
  • 이는 useState의 함수형 업데이트를 이용해 할 수 있다.

함수형 업데이트를 하게 되면, setUsers 에 등록하는 콜백함수의 파라미터에서 최신 users 를 참조 할 수 있기 때문에 deps 에 users 를 넣지 않아도 된다.

  • onChange 의 경우엔 함수형 업데이트를 해도 영향은 가지 않지만, 연습삼아 수정.
/* App.js */

function countActiveUsers(users) { /* ... */ }

function App() {
  // ...

  const onCreate = useCallback(() => {
    // ...
    setUsers(users => users.concat(user)); // 기존 코드: setUsers(users.concat(user));
    // ...
  }, [username, email]);

  const onRemove = useCallback(id => {
    setUsers(users => users.filter(user => user.id !== id));
  }, []);

  const onToggle = useCallback(id => {
    setUsers(users =>
      users.map(user =>
        user.id === id ? { ...user, active: !user.active } : user
      )
    );
  }, []);

  // ...
  return ( /* ... */ );
}

useCallback, useMemo, React.memo 는 컴포넌트의 성능을 실제로 개선할수있는 상황에서만 사용하도록 주의하자.

  • 예를 들어, User 컴포넌트의 <b><button>onClick 으로 설정해준 함수들은, useCallback 으로 재사용한다고 해서 리렌더링을 막을 수 없기 때문에 굳이 그렇게 할 필요 없다.
  • 추가로, 렌더링 최적화 하지 않을 컴포넌트에 React.memo 를 사용하는것은, 불필요한 props 비교만 하는 것이기 때문에 실제로 렌더링을 방지할 수있는 상황에만 사용해야한다.