
count와 text 두 개의 state를 가지고 있는 앱 컴포넌트가 있을 때, count state만 업데이트 하여도 자식 컴포넌트인 CountView 컴포넌트와 TextView 컴포넌트 둘 다 리랜더링 된다.

컴포넌트 리랜더링의 낭비를 방지하기 위해 자식 컴포넌트(CountView, TextView)에 각각 업데이트 될 조건을 걸어줄 수 있음
리액트 공식 문서
https://ko.reactjs.org/const MyComponent = React.memo(function MyComponent(props) { /* props를 사용하여 렌더링 */ }); React.memo는 고차 컴포넌트(Higher Order Component)입니다.
컴포넌트가 동일한 props로 동일한 결과를 렌더링해낸다면, React.memo를 호출하고 결과를 메모이징(Memoizing)하도록 래핑하여 경우에 따라 성능 향상을 누릴 수 있습니다. 즉, React는 컴포넌트를 렌더링하지 않고 마지막으로 렌더링된 결과를 재사용합니다.
실습
import { useState, useEffect } from "react";
const TextView = ({ text }) => {
return <div>{text}</div>;
};
const CountView = ({ count }) => {
return <div>{count}</div>;
};
const OptimizeTest = () => {
const [count, setCount] = useState(1);
const [text, setText] = useState("");
return (
<div style={{ padding: 30, border: `1px solid gray` }}>
<div>
<h2>count</h2>
{/* 자식 컴포넌트에 각각 count와 text를 prop으로 전달 */}
<CountView count={count} />
<button
onClick={() => {
setCount(count + 1);
}}
>
+
</button>
</div>
<div>
<h2>Text</h2>
<TextView text={text} />
<input
value={text}
onChange={(e) => {
setText(e.target.value);
}}
/>
</div>
</div>
);
};
export default OptimizeTest;

import { useState, useEffect } from "react";
const TextView = ({ text }) => {
// useEffect에 두 번째 인자로 배열을 받지 않았으니 모든 state가 변할때마다 실행됨
useEffect(() => {
console.log(`update === Text : ${text}`);
});
return <div>{text}</div>;
};
const CountView = ({ count }) => {
useEffect(() => {
console.log(`update === Count : ${count}`);
});
return <div>{count}</div>;
};
const OptimizeTest = () => {
const [count, setCount] = useState(1);
const [text, setText] = useState("");
return (
<div style={{ padding: 30, border: `1px solid gray` }}>
<div>
<h2>count</h2>
{/* 자식 컴포넌트에 각각 count와 text를 prop으로 전달 */}
<CountView count={count} />
<button
onClick={() => {
setCount(count + 1);
}}
>
+
</button>
</div>
<div>
<h2>Text</h2>
<TextView text={text} />
<input
value={text}
onChange={(e) => {
setText(e.target.value);
}}
/>
</div>
</div>
);
};
export default OptimizeTest;

--> count, text state가 변화할 때 마다 두 컴포넌트 모두 리랜더됨 (낭비 발생)
const TextView = React.memo(({ text }) => {
// 각 컴포넌트의 업데이트 상태 알아보기 위해 useEffect 추가
// useEffect에 두 번째 인자로 배열을 받지 않았으니 모든 state가 변할때마다 실행됨
useEffect(() => {
console.log(`update === Text : ${text}`);
});
return <div>{text}</div>;
});

import React, { useState, useEffect } from "react";
const CounterA = React.memo(({ count }) => {
useEffect(() => {
console.log(`AAAAA update === count: ${count}`);
});
return <div>{count}</div>;
});
const CounterB = React.memo(({ obj }) => {
useEffect(() => {
console.log(`BBBBB update === obj: ${obj}`);
});
return <div>{obj.count}</div>;
});
const OptimizeTest = () => {
const [count, setCount] = useState(1);
const [obj, setObj] = useState({
count: 1,
});
return (
<div style={{ padding: 30, border: `1px solid gray` }}>
<div>
<h2>count A</h2>
<CounterA count={count}></CounterA>
<button
onClick={() => {
setCount(count);
}}
>
A Button
</button>
</div>
<div>
<h2>count B</h2>
<CounterB obj={obj}></CounterB>
<button
onClick={() => {
setObj({
count: obj.count,
});
}}
>
B Button
</button>
</div>
</div>
);
};
export default OptimizeTest;

https://ko.reactjs.org/
props가 갖는 복잡한 객체에 대하여 얕은 비교만을 수행하는 것이 기본 동작입니다. 다른 비교 동작을 원한다면, 두 번째 인자로 별도의 비교 함수를 제공하면 됩니다.
function MyComponent(props) {
/* props를 사용하여 렌더링 */
}
function areEqual(prevProps, nextProps) {
/*
nextProps가 prevProps와 동일한 값을 가지면 true를 반환하고, 그렇지 않다면 false를 반환
*/
}
export default React.memo(MyComponent, areEqual);
- 얕은비교 : 객체의 값이 아니라 객체를 가르키고 있는 주소값을 비교하는 것.
import React, { useState, useEffect } from "react";
/* CounterB 컴포넌트에서 얕은 비교를 하지 않게 하여 최적화하기 */
const CounterB = ({ obj }) => {
useEffect(() => {
console.log(`BBBBB update === obj: ${obj.count}`);
});
return <div>{obj.count}</div>;
};
// 객체 동일 여부 판단하는 함수
const areEqual = (prevProps, nextProps) => {
// return true // prevProps === nextProps => 리랜더링 하지 않음
// return false // prevProps !== nextProps => 리랜더링 실행
if (prevProps.obj.count === nextProps.obj.count) {
return true;
}
return false;
};
// counterB에 React.memo() 적용한 컴포넌트 (state동일 여부에 따라 memoized 되는 컴포넌트)
// React.memo는 컴포넌트를 반환하는 고차 컴포넌트이기 때문에, MemoizedCounterB를 컴포넌트처럼 사용 가능
const MemoizedCounterB = React.memo(counterB, areEqual);
const OptimizeTest = () => {
const [count, setCount] = useState(1);
const [obj, setObj] = useState({
count: 1,
});
return (
<div style={{ padding: 30, border: `1px solid gray` }}>
<div>
<h2>count B</h2>
<MemoizedCounterB obj={obj} />
<button
onClick={() =>
setObj({
count: obj.count,
})
}
>
B Button
</button>
</div>
</div>
);
};
const OptimizeTest = () => {
const CounterA = React.memo(({ count }) => {
useEffect(() => {
console.log(`AAAAA update === count: ${count}`);
});
return <div>{count}</div>;
});
const CounterB = React.memo(({ obj }) => {
useEffect(() => {
console.log(`BBBBB update === obj: ${obj.count}`);
});
return <div>{obj.count}</div>;
});
const [count, setCount] = useState(1);
const [obj, setObj] = useState({
count: 1,
});
return (
...
)
