분류 Reactjs

React.memo()를 현명하게 사용하십시오.

컨텐츠 정보

  • 조회 358 (작성일 )

본문

사용자는 빠르고 반응이 빠른 사용자 인터페이스 (UI)를 즐깁니다. 100 밀리 초 미만의 UI 응답 지연은 사용자에게 즉시 느낍니다. 100 ~ 300 밀리 초 사이의 지연은 이미 감지 할 수 있습니다.


사용자 인터페이스 성능을 향상 시키기 위해 React는 Higher Order 구성 요소인 React.memo()를 제공합니다. 렌더링 된 출력을 메모함으로써 React는 불필요한 재 렌더링을 건너 뜁니다.


https://dmitripavlutin.com/use-react-memo-wisely/ 


이 글은 React.memo()가 퍼포먼스를 향상 시키는데 도움이 되는 상황을 구별하는데 도움이 됩니다. 또한 내가 알아야 할 유용한 메모 작성 팁을 설명 할 것입니다.


1. React.memo() 


DOM 업데이트를 결정할 때 React는 먼저 구성 요소를 렌더링 한 다음 그 결과를 이전 렌더링과 비교합니다. 렌더링 결과가 다르면 React가 DOM 업데이트를 결정합니다.


현재 대 이전 렌더링 결과 비교가 빠릅니다. 그러나 어떤 경우에는 프로세스 속도를 높일 수 있습니다.


구성 요소가 React.memo()로 래핑 될 때 React는 구성 요소를 렌더링하고 결과를 메모합니다. 다음 렌더링 전에 소품이 같으면 React는 메모 내용을 재사용합니다.


React 메모 예제를 살펴 보겠습니다. 기능 구성 요소 Movie는 React.memo()에 래핑됩니다.


export function Movie({ title, releaseDate }) {
  return (
    <div>
      <div>Movie title: {title}</div>
      <div>Release date: {releaseDate}</div>
    </div>
  );
}

export const MemoizedMovie = React.memo(Movie);


React.memo (Movie)는 새로운 MemoizedMovie 구성 요소를 반환합니다. 한 가지 차이점을 제외하고 원본 Movie 구성 요소와 동일한 내용을 출력합니다.


MemoizedMovie 렌더링 출력이 메모 됩니다. 메모 한 콘텐츠는 다음 렌더링 라운드에서 title 또는 releaseDate 소품이 동일하면 재사용 됩니다.


// First render. React calls MemoizedMovie function.
<MemoizedMovie 
  movieTitle="Heat"   releaseDate="December 15, 1995" />

// On next round React does not call MemoizedMovie function,
// preventing re-render
<MemoizedMovie
  movieTitle="Heat"   releaseDate="December 15, 1995" />

여기에 성능상의 이점이 있습니다. 메모를 작성한 결과를 다시 사용하면 React가 구성 요소를 다시 렌더링 하지 않고 가상 DOM 차이 검사를 수행하지 않습니다.


클래스 구성 요소와 동일한 기능이 PureComponent에 의해 구현됩니다.


1.1 props 맞춤형 평등 점검 


기본적으로 React.memo()는 소도구와 소품 객체의 얕은 비교를 수행합니다.


두 번째 인수를 사용하여 사용자 정의 동등 검사 기능을 나타낼 수 있습니다.


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

preEProps와 nextProps가 동일하면 areEqual (prevProps, nextProps) 함수는 true를 반환해야 합니다.


예를 들어 영화 구성 요소가 동일한 지 수동으로 계산해 봅시다.


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

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


moviePropsAreEqual() 함수는 prev 및 next props가 동일한 경우 true를 반환합니다.


2. React.memo()를 사용하는 경우 


Inforgraphic explaining when to use React.memo() 


2.1 구성 요소는 동일한 props으로 자주 렌더링 됩니다. 


React.memo()는 동일한 props을 가진 기능 구성 요소에 적용되어 동일한 출력을 렌더링 합니다.


React.memo()에서 구성 요소를 래핑하는 가장 좋은 경우는 기능 구성 요소가 자주 그리고 동일한 소품으로 렌더링 될 것으로 예상 할 때입니다.


동일한 소품으로 구성 요소가 다시 렌더링 되는 일반적인 상황은 상위 구성 요소에 의해 강제로 다시 렌더링 됩니다.


위에서 정의한 Movie 구성 요소를 다시 사용합시다. 새로운 상위 구성 요소 인 MovieViewsRealtime은 실시간 업데이트를 통해 동영상의 조회수를 표시합니다.


function MovieViewsRealtime({ title, releaseDate, views }) {
  return (
    <div>
      <Movie title={title} releaseDate={releaseDate} />      Movie views: {views}
    </div>
  );
}

응용 프로그램은 정기적으로 백그라운드에서 서버를 폴링하여 MovieViewsRealtime 구성 요소의 보기 속성을 업데이트합니다.


// Initial render
<MovieViewsRealtime 
  views={0}   title="Forrest Gump" 
  releaseDate="June 23, 1994" 
/>

// After 1 second, views is 10
<MovieViewsRealtime 
  views={10}   title="Forrest Gump" 
  releaseDate="June 23, 1994" 
/>

// After 2 seconds, views is 25
<MovieViewsRealtime 
  views={25}   title="Forrest Gump" 
  releaseDate="June 23, 1994" 
/>

// etc


views prop가 새로운 숫자로 업데이트 될 때마다 MovieViewsRealtime이 다시 렌더링 됩니다. title과 releaseDate가 같지 않아도 무비 리 렌더링이 실행됩니다.


Movie 구성 요소에 메모를 적용하는 것이 좋습니다.


memoized 구성 요소를 사용하기 위해 MovieViewsRealtime을 향상시킵니다. MemoizedMovie :


function MovieViewsRealtime({ title, releaseDate, views }) {
  return (
    <div>
      <MemoizedMovie title={title} releaseDate={releaseDate} />      Movie views: {views}
    </div>
  )
}

title과 releaseDate 소품이 같으면 React는 MemoizedMovie의 렌더링을 건너 뜁니다. 이렇게 하면 MovieViewsRealtime 구성 요소의 성능이 향상됩니다.


구성 요소가 동일한 소품으로 더 자주 렌더링 될수록 산출량이 많고 계산량이 많을수록 React.memo()에 포장해야 할 가능성이 커집니다. 


어쨌든 프로파일링을 사용하여 React.memo() 적용의 이점을 측정하십시오.


React.memo()가 성능을 향상 시키는 다른 상황을 알고 있습니까? 그렇다면 아래에 의견을 남겨주세요! 


3. React.memo()를 피할 때 


구성 요소의 렌더링 상황이 위에서 설명한 경우와 맞지 않으면 대부분 React.memo()가 필요하지 않습니다.


다음과 같은 경험 법칙을 사용하십시오. 성능 향상을 계량화할 수 없는 경우 메모를 사용하지 마십시오.


성능 관련 변경 사항이 잘못 적용되면 성능이 저하 될 수도 있습니다. React.memo()를 현명하게 사용하십시오. 


기술적으로는 가능하지만 React.memo()에서는 클래스 기반 구성 요소를 래핑 하는 것이 적절하지 않습니다. PureComponent 클래스를 확장하거나 클래스 기반 구성 요소에 대한 메모가 필요한 경우 shouldComponentUpdate() 메서드의 사용자 정의 구현을 정의하면 됩니다.


3.1 쓸데없는 props 비교 


일반적으로 다른 소품으로 렌더링 되는 구성 요소를 상상해보십시오. 이 경우 메모 작성은 이점을 제공하지 않습니다.


React.memo()에서 이러한 휘발성 구성 요소를 래핑 할지라도 React는 모든 재 렌더링시 2 개의 작업을 수행해야 합니다.

  1. 비교 함수를 호출하여 이전 및 다음 소품이 같은지 여부 확인
  2. 소품 비교가 거의 항상 false를 반환하기 때문에 React는 이전 및 현재 렌더링 출력의 diff를 수행합니다.

비교 함수의 호출은 거의 항상 false를 반환하기 때문에 쓸모가 없습니다.


4. React.memo() 및 콜백 함수 


함수 객체는 "일반"객체와 동일한 비교 원칙을 따릅니다. 함수 객체는 자신에게만 동일합니다.


몇 가지 기능을 비교해 보겠습니다.


function sumFactory() {
  return (a, b) => a + b;
}

const sum1 = sumFactory();
const sum2 = sumFactory();

console.log(sum1 === sum2); // => falseconsole.log(sum1 === sum1); // => true
console.log(sum2 === sum2); // => true


sumFactory()는 팩토리 함수입니다. 2 개의 숫자를 더하는 화살표 함수를 반환합니다.


함수 sum1과 sum2는 공장에서 생성됩니다. 두 함수 모두 2 개의 숫자를 더합니다. 그러나 sum1과 sum2는 서로 다른 함수 객체입니다.


부모 구성 요소가 자식에 대한 콜백을 정의하면 암시 적 새 함수 만들기가 발생할 수 있습니다. 이 방법으로 메모 작성이 중단되고 이를 수정하는 방법에 대해 알아 보겠습니다.


다음 구성 요소 로그 아웃은 logout에 콜백 소품을 허용합니다.


function Logout({ username, onLogout }) {
  return (
    <div onClick={onLogout}>
      Logout {username}
    </div>
  );
}

const MemoizedLogout = React.memo(Logout);

함수 평등 함 (function equality pitfall) 때문에 콜백을 허용하는 구성 요소는 메모를 적용 할 때 주의해서 처리해야 합니다. 부모 컴포넌트가 모든 재 렌더링시 콜백 함수의 다른 인스턴스를 제공 할 가능성이 있습니다.


function MyApp({ store, cookies }) {
  return (
    <div className="main">
      <header>
        <MemoizedLogout
          username={store.username}
          onLogout={() => cookies.clear()}        />
      </header>
      {store.content}
    </div>
  );
}

동일한 사용자 이름 값이 제공 되더라도 MemoizedLogout은 onLogout 콜백의 새 인스턴스를 받기 때문에 매번 다시 렌더링됩니다.


메모가 깨졌습니다.


이 문제를 해결하려면 동일한 콜백 인스턴스를 사용하여 onLogout prop를 설정해야 합니다. 렌더링간에 콜백 인스턴스를 보존하기 위해 useCallback()을 적용 해 보겠습니다.


const MemoizedLogout = React.memo(Logout);

function MyApp({ store, cookies }) {
  const onLogout = useCallback(() => { cookies.clear() }, []);  return (
    <div className="main">
      <header>
        <MemoizedLogout
          username={store.username}
          onLogout={onLogout}        />
      </header>
      {store.content}
    </div>
  );
}


useCallback (() => {cookies.clear ()}, [])은 항상 동일한 함수 인스턴스를 반환합니다. MemoizedLogout의 메모가 수정되었습니다.


5. React.memo()는 성능 힌트입니다. 


엄밀히 말하면, React는 성능 힌트로 메모를 사용합니다.


대부분의 상황에서는 React가 메모를 작성한 구성 요소를 다시 렌더링 하는 것을 피할 수 있지만 렌더링 하지 못하도록 해야 합니다.


6. 결론 


React.memo()는 기능 구성 요소에 대한 메모 작성의 이점을 얻는 데 유용한 도구입니다. 올바르게 적용되면 다음 소품이 이전과 동일 할 때 구성 요소 재 렌더링을 방지합니다.


콜백 함수가 있는 소품을 사용하는 구성 요소를 메모 할 때 주의하십시오. 렌더링간에 동일한 콜백 함수 인스턴스를 제공해야 합니다.


메모 작성의 성능 향상을 측정하기 위해 프로파일링을 사용하는 것을 잊지 마십시오.