혼자 적어보는 노트

[React] 리렌더링 조건 / React memo 본문

React

[React] 리렌더링 조건 / React memo

jinist 2022. 1. 14. 14:25

React로 프로젝트를 만들다보니

너무 많은 리렌더링을 하고 있는것 같다는 생각이 들 때가 있었다.

적절하게 사용 시 성능향상에 도움이 되는 React.memo() 에 대해 적어보겠다.

 

먼저 React에서 리렌더링이 되는 조건에 대하여 알아보자.

 

React의 리렌더링 조건

 

1. state가 업데이트 되었을때

리액트에서는 state의 변경이 감지되면 리렌더링을 해주지만 setState가 아닌 직접 state변경을 하면

state변경을 감지하지 못해 리렌더링이 되지 않는다.

 

2. 전달 받은 props값이 업데이트 되었을 때.

하위컴포넌트에 props값을 전달 했을 때 그 값이 변경된다면 리렌더링이 된다.

 

2. 부모 컴포넌트가 리렌더링 되었을 때

부모컴포넌트가 리렌더링 되었을 때 자식컴포넌트 또한 리렌더링이 된다.

 

이 외에도 shouldComponentUpdate 에서 true를 반환하거나

forceUpdate()로 강제 리렌더링을 시켰을 때가 있다.

 

 

리액트에서는 렌더링을하면서 이전의 값과 달라진 값이 있다면

업데이트를 해주는데, 변경된 값이 없음에도 불구하고

부모컴포넌트가 리렌더링 되었다는 이유, 참조하지도 않는 부모컴포넌트의 state가 업데이트 되었다는 이유로

컴포넌트들에게 잦은 리렌더링이 발생할 경우 성능 문제로 이어질 수 있다.

 

 

아래의 작은 예시를 살펴보자.

 

import React, { useState } from "react";
import UserProfile from "./components/UserProfile";

function App() {
  const data = { id: 1, name: "Jay", age: 29 };
  const [userData, setUserData] = useState(data);

  const increaseAge = () => {
    setUserData({ ...userData, age: userData.age + 1 });
  };
  return (
    <div>
      <UserProfile name={userData.name} />
      <h2>{userData.age}</h2>
      <button onClick={increaseAge}> + </button>
    </div>
  );
}

export default App;

 

import React from "react";

const UserProfile = ({ name }) => {
  console.log(name);
  return (
    <div>
      <li>
        <p>Name : {name}</p>
      </li>
    </div>
  );
};

export default UserProfile;

userData에는 id와 name, age의 정보가 담겨있다.

버튼을 클릭하면 age가 1씩 증가하고

UserProfile에는 props로 userData.name를 받아서 data의 name을 사용하고 있다.

이 때 상위 컴포넌트인 App에서 버튼을 클릭하여 state를 변경할 경우

하위컴포넌트인 UserProfile 또한 리렌더링이 되어 UserProfile에 있는 console.log가 출력되는 것을 확인할 수 있다.

 

state의 age만 변경되는데 name만을 참조하는 UserProfile이 리렌더가 될 필요는 없다.

 

React.memo 사용

import React from "react";

const UserProfile = ({ name }) => {
  console.log(name);
  return (
    <div>
      <li>
        <p>Name : {name}</p>
      </li>
    </div>
  );
};

export default React.memo(UserProfile);

위와 같이 UserProfile에 React.memo로 래핑을 하면

해당컴포넌트의 결과를 메모이징(memoizing) 하고 다음 렌더링이 일어날 때의 props와 비교를 한다.

props가 같다면 메모이징 한 내용을 재사용 한다.

즉 name이 변경되지 않는다면 메모이징된 데이터를 재사용하여 리렌더링을 잡아주어

age증가 시 UserProfile의  console.log가 출력되지 않는다.

 

위의 예시에서는 Userdata의 name만 props로 넘겨주었는데.

<UserProfile data={userData} />

만약 UserData를 전부 넘겨준다면,

age가 변경될 때 userData가 변경되는 것이기 때문에

React.memo로 래핑을 했더라도 UserProfile의 컴포넌트가 리렌더링된다.

 

 

React.memo() 비교방식 커스터마이징

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

React.memo()의 첫 번째 매개변수로 컴포넌트, 두 번째 매개변수로 비교함수를 만들어서

비교하는 방식을 수정할 수 있다.

prevProps와 nextProps가 같다면 true를 반환하고 값이 true라면 메모이징된 내용을 재사용한다.

 

const UserProfile = ({ data }) => {
  console.log(data.name);
  return (
    <div>
      <li>
        <p>Name : {data.name}</p>
      </li>
    </div>
  );
};

function UserPropsAreEqual(prevProps, nextProps) {
  return prevProps.data.name === nextProps.data.name;
}

export default React.memo(UserProfile, UserPropsAreEqual);

 

React.memo()를 사용해야 할 때

같은 props로 자주 렌더링이 될 경우, 크고 복잡한 연산이 있는 경우에 사용하기에 좋다.

props가 자주 변경되는 컴포넌트의 경우 React.memo()로 래핑 시

비교연산을 하고 false를 반환하는 횟수가 많기 때문에

props의 비교함수가 불필요하다. 그렇기 때문에 무분별하게 React.memo()를 사용하는 것은 좋지 않다.

(class컴포넌트의 경우 PureComponent, shouldComponentUpdate()를 사용하자)

Comments