본문 바로가기

공부기록/[강의노트] Udemy React 완벽가이드 101~200

# 163 [udemy React 완벽 가이드 노트] React.memo , useCallback

개발자는 특정한 상황일 경우에만 

컴포넌트를 재실행 하도록 

리액트에 지시할 수 있다. 

 

(예를 들어 props가 실질적으로 변경될 경우)

 

함수형 컴포넌트에 export default 다음 오는 컴포넌트 명을 React.memo로 감싸주기만 하면 된다. 

 

그러면 리액트는 어떤 props가 입력되는지 확인하고

모든 props의 신규 값을 확인하여 

기존 props 값과 비교하도록 한다. 

 

props값이 변경된 경우에만 

컴포넌트를 재실행, 재평가 하게 되는 것이다. 

부모 컴포넌트가 변경되어도 컴포넌트의 props값이 바뀌지 않았다면 

컴포넌트 실행은 되지 않는다. 

 

 

 

 

모든 컴포넌트에 React.memo를 하지 않는 이유


memo 메소드는 App에 변경이 발생할 때마다 

memo 메소드를 사용한 컴포넌트로 이동하여 

기존 props값과 새로운 값을 비교하게 한다. 

 

 

그러려면 기존 props를 저장할 공간이 필요하고 비교하는 작업이 필요하다 

그러므로 컴포넌트를 재평가하는 것과

props를 비교하는 성능비용을 서로 맞바꾸는 것이다. 

 

 

그러면 props의 갯수, 컴포넌트 복잡도, 자식 컴포넌트의 숫자에 따라

React.memo가 컴포넌트 재평가 보다 나을 수도 있고 아닐 수도 있는 것이다. 

 

자식 컴포넌트가 더 많으면 React.memo를 쓰는 편이 나을 수 있다. 

 

한편, 부모 컴포넌트를 재평가 할 때마다

컴포넌트의 변화가 있거나 

props의 값이 변화 할 수 있는 경우라면 React.memo는 효과가 거의 없을 수 있다. 

 

파일 크기가 작은 경우에도 필요가 없다. 

 

 

React.memo가 적용이 안되는 이유


App  컴포넌트에서 모든 함수는 실행될 때마다 재생성 된다. 

모든 상수도 재생성된다. 

자식 컴포넌트에 props가 전달되고 있는 경우 

하드코딩을 했다고 하더라도 계속 새로운 props값이 전달되고 있는 것이다. 

 

이 비교는 리액트를 사용해서 하는 것이 아니라 

자바스크립트를 기반으로 수행하는 것이다. 

 

자바스크립트에서

원시값은 false === false 하면 true가 나오고 'hi' === 'hi' 하면 true가 나온다. 

하지만 배열의 경우는 다르다. 

 

[1,2,3] === [1,2,3] false 가 나온다.  

함수는 하나의 객체에 불과하다. 

 

그러므로 함수가 props로 전달되고 있는 경우에는 

React.memo를 적용시키더라도 계속 새로운 함수로 인식하기 때문에

재평가후 렌더링을 하게 된다.  

 

그럴 땐 useCallback을 같이 사용하도록 하자. 

 

 

 

useCallback


useCallback훅은 컴포넌트 실행 전반에 걸쳐 

함수를 저장 할 수 있게 하는 훅이다. 

 

리액트에 함수를 저장해 매번 실행할 때마다 함수 재생성이 필요 없다는 걸 

알릴 수 있게 된다. 

그러면 동일한 함수가 메모리의 동일한 위치에 저장 되므로 비교작업을하여 

 

변경될 경우에만 실행되게 할 수 있는 것이다. 

 

함수 변수에 useCallback을 넣어 첫번 째 인자로  함수를 전달하면 된다. 

두번째 인자로 배열을 넣어야 한다.  ( useCallback호출에 대한 의존성 배열)

상태, props, 컨텍스트를 지정할 수 있다. 

 

[ ] 이렇게 두면 절대 변경되지 않을 것이라고 리액트에게 알려주는 것이다. 

그러면 App컴포넌트가 다시 렌더링 되어도 항상 같은 함수 객체가 사용된다.

const toggle = useCallback(() => {
    setShowParagraph((prev) => !prev);
}, []);

 

 

useCallback의 dependencies


자바스크립트의 함수는 모두 클로저이다. 

(자바스크립트에서 함수가 정의되면 함수외부에서 사용하는 모든 변수를 잠그게 된다

함수가 실행되면 저장된 상수를 그대로 사용한다. 

변수의 값은 변수가 저장된 시점의 값을 사용한다. 

 

useCallback을 사용하여 리액트에게 해당 함수를 저장하라고 할  수 있다. 

아래의 경우 if 다음 함수를 메모리 어딘가에 저장해 두었다. 

const toggleParagraphHandler = useCallback(() => {
  if (allowToggle) {
    setShowParagraph(prevShowParagraph => !prevShowParagraph);
  }
}, []);

 

이제 App함수가 토글상태가 변경되어 재평가와 재실행이 된다면

리액트는 이 함수를 재생성하지 않는다. 

그러므로 allowToggle 변수는 App컴포넌트가 실행될 시점(함수생성 시점)의 오래된 값이다. 

 

 

dependencies 사용하지 않을 때 문제점 :


함수 재생성을 필요로 할 때 재생성을 하지 않게 된다. 

이 함수에서 사용하는, 함수 외부에서 오는 값이 변경되더라도 

오래된 시점의 값만 사용하게 되면 문제가 될 수 있다. 

 

dependencies에  allowToggle을 추가하게 되면, 

useCallback으로 인해 

함수가 App컴포넌트가 실행될 시점에만 새로 생성이 되게 되는데, 

allowToggle값이 변경될 때마다 

새로 함수를 재생성하여 새로 만든 함수를 useCallback을 사용하여 저장할 수 있게 되는 것이다. 

 

 

함수에 allowToggle값의 최신 값만을 사용할 수 있게 된다.