React.memo(함수형 컴포넌트)
: 해당 컴포넌트에 어떤 props가 입력되는지 확인하고, 입력 되는 모든 props의 신규 값을 확인한 뒤 이를 기존의 props의 값과 비교하도록 리액트에게 전달한다. 그리고 props의 값이 바뀐 경우에만 컴포넌트를 재실행 및 재평가하게 된다.👾 App.js
const App = ()=>{
const [isShow, setIsShow] = useState(false);
const clickHandler = ()=>{
// toggle
setIsShow(prev => !prev);
}
return(
<>
// state인 isShow가 아닌 false로 고정됨.
<DemoOutput show={false}/>
<Button onClick={clickHandler}>toggle</Button>
</>
);
}
👾 DemoOutput.js
import MyParagraph from './MyParagraph';
const DemoOutput = (props) => {
return <MyParagraph>{porops.show ? 'This is new!' : ''}</MyParagraph>;
}
export default React.memo(DemoOutput);
👉🏻 버튼 클릭 시 state가 변경되어 부모 컴포넌트(App)는 재평가가 일어나고 재평가가 일어나면서 jsx가 리턴하는데, 그 과정에서 자식 컴포넌트(DemoOutput)도 재평가가 일어나야하지만 React.memo(DemoOutput)
로 인하여 DemoOutput의 props는 변경되지 않았기 때문에(현재 false로 고정되어 있음) DemoOutput컴포넌트는 재평가가 일어나지 않음!!
🔥 불필요한 재렌더링을 피하여 최적화가 이루어지고 있음!!
👾 Button.js
const Button = (props) => {
return <button onClick={props.onClick}>{props.children}</button>;
}
export default React.memo(Button);
👉🏻 부모컴포넌트는 함수이고, 재평가가 실행될 때마다 해당 함수는 재생성이 된다. props로 전달한 함수(onClick)의 값(함수의 주소를 가리키는 별칭)이 갱신되면서 React.memo()는 props가 변경된 것으로 판단하여 자식 컴포넌트(Button)를 재평가하게 된다!!! 이것은 리액트의 오류다!!
👉🏻 함수의 재생성을 막기위해선 useCallback 훅을 사용하면 된다.
useCallback(매핑할 함수,[dependency])
[dependency]
: 이 환경에서 사용할 수 있는 값에 클로저를 만들게 된다.👾 App.js
const App = ()=>{
const [isShow, setIsShow] = useState(false);
//useCallback 훅 사용
const clickHandler = useCallback(()=>{
setIsShow(prev => !prev);
}, []);
return(
<>
<DemoOutput show={false}/>
<Button onClick={clickHandler}>toggle</Button>
</>
);
}
👉🏻 위 예제에서 clickHandler를 useCallback훅 사용할 경우 버튼을 클릭해도 Button컴포넌트는 재평가가 되지 않는다!!(React.memo 사용)
: isAllow라는 토글버튼의 동작을 제어하는 state를 설정한 경우이다.
👾 App.js
const App = ()=>{
const [isShow, setIsShow] = useState(false);
const [isAllow, setIsAllow] = useState(false);
//useCallback 훅 사용
const clickHandler = useCallback(()=>{
if(isAllow === true){
setIsShow(prev => !prev);
}
}, [isAllow]);
const allowClickHandler = ()=>{
setIsAllow(true);
}
return(
<>
<DemoOutput show={false}/>
<Button onClick={clickHandler}>toggle</Button>
<Button onClick={allowClickHandler}>allow button</Button>
</>
);
}
👉🏻 useCallback()의 의존성 배열을 빈값으로 둘 경우, useCallback으로 인하여 해당 함수(clickHandler)는 isAllow값이 변경되어도 재평가가 되지 않아 토글기능이 실행되지 않는다.
👉🏻 의존성으로 isAllow을 추가해야 isAllow가 변경될 때 함수안의 isAllow가 최신값으로 업데이트가 되어, 해당 기능이 잘 실행된다!
👉🏻 이때 clickHandler는 클로저이고, isAllow는 자유변수이다!
useMemo(함수, [dependency])
: 버튼 클릭 시 제목이 변경되어 자식 컴포넌트인 DemoList도 재평가되어 props로 받은 item들의 정렬 작업을 다시하게 된다.
: 특히 정렬은 컴포넌트에서 수행 가능한 대표적인 성능 집약적 작업 중 하나이다.
: 이것을 해결하기 위해 useMemo훅을 사용하는 예제이다.
👾 App.js
const App = () => {
const [listTitle, setListTitle] = useState('My List');
// useCallback() 사용
const changeTitleHandler = useCallback(()=>{
setListTitle('New Title');
},[]);
return (
<div>
<DemoList title={listTitle} items={[5, 3, 1, 10, 9]} />
<Button onClick={changeTitleHandler}>Change List Title</Button>
</div>
);
}
👾 Button.js
const Button = (props) => {
return <button onClick={props.onClick}>{props.children}</button>;
}
// React.memo() 사용
export default React.memo(Button);
👾 DemoList.js
const DemoList = (props) => {
const sortedList = props.items.sort((a,b) => a - b );
return (
<div>
<h2>{props.title}</h2>
<ul>
{sortedList.map((item)=>(
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}
// React.memo() 사용
export default React.memo(DemoList);
👾 App.js
const App = () => {
const [listTitle, setListTitle] = useState('My List');
// useCallback() 사용
const changeTitleHandler = useCallback(()=>{
setListTitle('New Title');
},[]);
return (
<div>
<DemoList title={listTitle} items={ useMemo(() => [5, 3, 1, 10, 9], []) } />
<Button onClick={changeTitleHandler}>Change List Title</Button>
</div>
);
}
👉🏻 items는 배열인 참조값이므로 App이 재평가 될때마다 메모리상 다른값이 된다.
👉🏻 useMemo()를 사용하여 items를 리액트의 내부 저장 공간에 저장한다.
👉🏻 이때 빈의존성 배열은 외부 의존성이 없기때문에, 값은 변함이 없는 하드코딩이 된다.
👾 DemoList.js
const DemoList = (props) => {
const { items } = props;
const sortedList = useMemo(()=>{
return items.sort((a,b) => a - b );
}, [items]);
return (
<div>
<h2>{props.title}</h2>
<ul>
{sortedList.map((item)=>(
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}
// React.memo() 사용
export default React.memo(DemoList);
👉🏻 item에 변경사항이 발생할 때만 리빌드를 진행하게 됨!!!
👉🏻 버튼을 눌러도 처음 이후로 배열이 재정렬 되지 않음!
[참고] Udemy - React 완벽 가이드 with Redux, Next.js, TypeScript