React.memo는 Higher-Order Components(HOC)로, 컴포넌트를 인자로 받아서 새로운 컴포넌트를 다시 반환해주는 함수를 말한다.
일반적인 컴포넌트는 인자로 받은 props를 UI를 그리는데 활용하는 반면, HOC는 인자로 받은 컴포넌트를 새로운 별도의 컴포넌트로 만든다.
const Greeting = memo(function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
});
export default Greeting;
// or
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
});
export default React.memo(Greeting);
만약 컴포넌트가 같은 props받을때 같은 결과를 렌더링한다면 React.memo를 사용해 불필요한 컴포넌트의 리렌더링을 방지할 수 있다.
즉, 컴포넌트에 동일한 props가 들어온다면 리액트는 컴포넌트 렌더링 과정을 스킵하고, 마지막에(latest)에 렌더링된 결과를 재사용한다.
이를 메모이제이션이라고 했던가..?
또 React.memo는 오직 props가 변경되었는지 아닌지만 확인한다.
만약에 React.memo에 감싸진 함수형 컴포넌트가 함수 내부에서 useState나 useContext같은 훅을 사용하고 있다면, state, context가 변경될때마다 리렌더링이 된다.
(생각해보면 이건 당연한듯..?? 바뀐건 그려줘야지 ㅋㅋ)
기본적으로 props로 들어온 객체들은 얕은비교를 이용해서 비교하기 때문에, 이런 비교 방식을 바꾸고 싶다면 직접 비교 함수를 만들어서 넘겨줄 수 있다.
function MyComponent(props) {
/* 컴포넌트 렌더링 코드 */
}
function areEqual(prevProps, nextProps) {
/*
만약 전달되는 nextProps가 prevProps와 같다면 true를 반환, 같지 않다면 false를 반환
*/
}
export default React.memo(MyComponent, areEqual);
여튼! 얕은복사를 이용해서 비교하기 때문에 propsfh number, string같은 scarlar값은 실제 값이 동일한가를 비교하지만,
객체의 경우는 스칼라값과 달리 내용물이 아닌 주소값만을 비교하게 된다!
따라서 같은 값들이 들어있는 다른 주소의 객체가 들어가게 되면, 두 요소는 같지 않게 되어서 렌더링이 발생하게 된다~
const a = {
name: "hello"
}
const b = {
name: "hello"
}
props가 a가 들어왔다가 이후에 b가 들어온다면, 참조하는 주소값이 다르기에 렌더링이 이뤄진다는 의미.
useMemo는 메모이즈된 값을 반환하는 훅이다.
인자로 함수와 의존값(dependencies)를 받는데, 두번째 인자로 넣어준 디펜던시값 중 하나라도 변경되면 값을 재계산한다. (useEffect같은 느낌)
이를통해서 매 렌더링이 발쌩할때마다 소요되는 불필요한 계산을 피할 수 있다.
만약 dependencis array로 아무것도 전잘되지 않는다면, 랜더링시마다 항상 값을 새롭게 계산해 반환한다. (이것도 useEffect 같네?)
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
위 코드는 a와 b의 값이 변할때만 첫번째 인자로 들어온 함수가 실행되어 재계산이 되고, 그렇지 않으면 cached된 값을 반환한다.
또 첫번째 인자로 받는 함수를 인자를 받지 않는 함수여야 한다~
React.memo와 useMemo 모두 props가 변하지 않으면(즉 이전 props와 동일하면) 인자로 넘긴 함수는 재실행되지 않고, cached된 결과를 반환한다는 공통점이 있다.
/*별도로 두번째 인자를 넘기지 않을 경우 props가 변하지 않는다면 재렌더링 되지 않음*/
const NameTag = React.memo(
(props) => <div>{props.name}</div>
);
/*만약 두번째 인자로 특정 props.name값이 같지 않을때만 재렌더링 하도록 커스텀 비교 함수를 넣어주고 싶을 때*/
const NameTag = React.memo(
(props) => <div>{props.name}</div>
,
(prevProps, nextProps) => prevProps.name === nextProps.name
)
function NameTag(props) {
return useMemo(
() => <div>{props.name}</div>
,
[props.name]
)
}