코드노트

REACT Hooks 노트 React.memo (feat.useMemo, useCallback) 본문

Code note/리액트

REACT Hooks 노트 React.memo (feat.useMemo, useCallback)

코드노트 2023. 4. 2. 03:29

useMemo가 아니라 React.memo? memo는 useMemo만 있는게 아니였나.. 했지만

React.memo도 있었다..

 

쉽게말하면 불필요한 컴포넌트 렌더링을 방지하는 훅! 그럼 useMemo와는 어떻게 다를까?

React.memo와 useMemo, useCallback을 모두 정리해보려고 한다.



useMemo

const value = useMemo(() => {
	return 함수()
}, [item])

------------------------

const value = useMemo(() => {
	return 함수()
}, [])

- 첫 번째 인자는 콜백함수를 받는다. 두번 째 인자는 의존성 배열(Dependency)을 받으며,

두번째 인자인 배열의 요소의 값이 업데이트 될  때만 콜백 함수를 다시 호출한다.

 

* useMemo는 1ms이상 걸리는 경우 사용하자.

 

- 여기서 두번째 인자에 빈 배열을 넘겨주게 되면 처음 컴포넌트가 마운트 되었을때만 값을 계산하고 이후에는 항상 memoization값을 꺼내 와서 사용하게 된다.


useMemo 사용 예제

- 객체 useMemo

country: isNationality ? "한국" : "외국"

- 객체타입의 값이 변경될때에는 메모리상의 변경이 발생한다. ( 같은 값이라도 메모리상의 공간은 다르기 때문 )

그렇기 때문에 컴포넌트가 객체값이 할당될때에는 메모리상의 변화를 감지하게되고,

useEffect가 의존하고 있는 location은 변화한걸로 간주하여 useEffect내부 로직이 실행된다.

 

- 즉 number값이 변할때마다 리렌더링이 되면서 location의 값도 변경되고 useEffect가 실행되는 것이다.

 

- 이럴때 사용할 수 있는게 useMemo이다.

useMemo를 사용하게 되면 useMemo가 의존하고있는 isNationality가 변경될때마다 리렌더링이 되고 그 외에는 값이 변하지 않는다.

 

- 만약 컴포넌트내부에서 서로 연결되어있지 않고 각각의 상태를 사용하고 있다면 useMemo를 통해서 리렌더링이되는걸 막을 수 있다.



useCallback

- useMemo와 똑같이 내부에 useCallback으로 전달한 콜백함수를 memoization하여 사용해야할때 함수를 사용할 수 있다.

 

* 가급적 child에게 내려주는 prop의 경우에 사용하자!

 

리액트 함수형 컴포넌트는 함수이다.

함수형 컴포넌트가 렌더링이 된다는것은? -> 컴포넌트 함수가 호출되는 것! -> 그럼 모든 내부 변수가 초기화된다.

 

그렇다면 컴포넌트 내부에서 사용하는 함수를 useCallback을 사용해준다면,

컴포넌트가 호출될때마다 함수를 다시호출하지않고 첫 마운트되었을때 사용한 함수를 그대로사용한다.

 

useCallback(() => {
	return value
}, [item])

 

- 2개의 인자를 받으며, memoization할 함수와 의존성 배열(Dependency)을 사용한다.

- 의존성 배열값이 변경될때마다 함수를 재실행한다.

 


useCallback 사용 예제

 

- 현재 예제에서는 input값을 올리게되면 someFunc는 실행되지 않는다.

true,false버튼을 누르게 되면 number의 값이 반영되고 CallsomeFunc버튼을 눌렀을때 console에 변경된 값이 얼마인지 알 수 있다.

 

- useCallback을 사용하지 않았다면 어떻게 될까?

- input에서 number값을 올리고 내리거나, true, false버튼을 누를때도 someFunc는 실행되기 때문에 불필요한 렌더링이 계속 반복되게 된다.

- 이와같이 함수를 할당하는것도 상태가 변화되는것이기 때문에 같은 함수라도 리렌더링이 될때마다 계속 변화를 감지하고 호출하게 되는것이다.

- 이를 useCallback을 통해서 불필요하게 변하지않은 함수의 리렌더링을 막을 수 있다.


 

- 이 예제에서는 isDark상태가 바뀔때마다 리렌더링이 되기 때문에 useCallback을 사용하지 않는다면

createBoxStyle 함수가 계속 실행되게 된다.

- Box크기를 조절하는것과는 필요가 없는 함수이지만 마운트될때마다 컴포넌트들이 리렌더링되면서

isDark의 state가 변경되면서 다시 리렌더링이 진행되고 createBoxStyle함수도 재할당되며 변화가 된것처럼 실행된다.

- useCallback을 통해서 size의 상태를  의존성 배열(Dependency) 에 넣어주면 해결!

 



React.memo

고차컴포넌트 HOC(HigherOrderComponent)

일반 컴포넌트를 React.memo를 통하게 되면 Prop Check를 통하여 자신이 받는 props의 변화가 있는지를 확인한다.

- 렌더링이 될때마다 Prop Check를 통해서 props가 변화가 있는지 없는지를 확인 후 변화가 있다면 리렌더링이 된다.

 

 

- memoization된 값을 반환하는 훅

- prop Check를 통해서 변화가 없는 값이면 리렌더링이 되는게 아닌 값을 재사용한다.

- 불필요한 렌더링을 막을 수 있는 훅!

 

 

state가 업데이트가 되면 Atomic과 같은 디자인 패턴을 적용한 경우라면?

여러 컴포넌트로 구성되어 있는 페이지는 하나의 state가 업데이트되지만

다른 컴포넌트들도 리렌더링이 될 것이다.

 

어 그럼 모든 props를 React.memo를 통해 Prop Check해주면 좋지 않을까?라고 생각할 수 있다.

React.memo는 변화된값과 비교해야하기때문에 값을 저장해두게 된다.

무분별하게 사용하게 되면 메모리를 추가적으로 소비하게 된다!

 

 

꼭 필요할때만 사용하자!

그럼 어쩔때 사용해야할까?

- 컴포넌트가 같은 props로 자주 렌더링이 될때

- 컴포넌트가 렌더링이 될때마다 복잡한 로직을 처리해야할 때

 

 

React.memo는 Props변화에만 의존하는 최적화 방법

useState, useReducer, useContext와 같은 상태관리 훅을 사용한다면

props의 변화가 없다라도 다시 렌더링이 된다. 기억하자!


React.memo 예제

- Child컴포넌트는 childAge State를 props로 받고있다.

parent age를 증가시키는 버튼을 누르게 되면 Child컴포넌트도 리렌더링이 될것이다.

- react.memo는 컴포넌트의 props가 변하지 않는다면 리렌더링을 막아준다. Child컴포넌트를 memo(Child)로 사용하게되면

parent age를 증가시키는 버튼을 누르더라도 Child에게 전해지는 childAge props는 변화가 없기 때문에 리렌더링을 막을 수 있다.

- console을 확인해보면 Child컴포넌트에서 나오는 console은 child age 버튼을 누를때만 실행되는걸 볼 수 있다.

 

 

그럼 useCallback, useMemo를 조합하여 최적화를 시켜보자!

 

- 위 예제에서 객체를 추가하고 함수도 새로 추가하였다. 

- React.memo를 통해 Child컴포넌트를 최적화시켰지만 memo는 컴포넌트의 props의 변화만 감지하여

prop check를 진행한다. 

 

*  여기서 꼭 알아야할 것은 객체는 항상 렌더링이 되면서 할당을 할때 메모리값이 변경된다.

* 함수 또한 같은값을 반환하는 순수함수라도 함수또한 객체이기때문에 메모리값이 변경된다.

 

- 그럼 name은 객체, tellme는 함수이기 때문에 props로 전달하는 값은 변경이 된것처럼 감지된다.

- 그렇기 때문에 name객체는 useMemo를 사용하여 변하지 않는 값을 memoization을 시켜주고,

tellme함수또한 useCallback으로 memozaition을 시켜주면 불필요한 렌더링을 줄일 수 있었다.


useMemo, useCallback 그리고 React.memo는 꼭 필요할때만 사용해야하고

사용하기전에 꼭 필요할까?라는 생각을 다시한번 해보자..!

불필요한 렌더링을 막을 수는 있지만 객체, 함수, 컴포넌트를 따로 저장하면서 메모리를 사용하기때문에

그또한 불필요한 memoization이 될 수 있다는걸 명심하자.!!!