코딩.zip
[React] 재렌더링 구조 & 메모이제이션 활용 최적화 본문
🐾 재렌더링 발생 조건
- state가 업데이트 된 컴포넌트
- props가 변경된 컴포넌트
- 재렌더링 된 컴포넌트 아래의 모든 컴포넌트
☻ 사전준비
실습 구조
src
├── App.js
├── components
│ ├── Child1.jsx
│ ├── Child2.jsx
│ ├── Child3.jsx
│ └── Child4.jsx
├── index.css
├── index.js
🔔 메모이제이션(memoization)
- 함수의 반환 값을 저장해 두었다가 동일한 인수(argument)가 전달될 때, 이전에 계산한 값을 재사용하는 최적화,고속화 기법
✔︎ 컴포넌트의 메모이제이션 - memo
: 컴포넌트의 props가 바뀌지 않았다면, 리렌더링하지 않도록 설정하는 함수
- App 컴포넌트가 재렌더링될 때, memo를 사용하여 감싼 경우, 이전에 렌더링된 결과를 재사용
- 이전에 같은 인수로 렌더링된 결과가 이미 캐시되어 있기 때문에 새로 렌더링하지 않고 이전에 계산된 결과를 사용
- 변경된 상태에 의존하지 않는 하위 컴포넌트를 새로 렌더링하지 않음 ➡️ Child4
- 컴포넌트 내부에서 useState같은 훅을 사용할 때 상태가 변경 되면 리렌더링
✔︎ 변수의 메모이제이션 - useMemo
: 첫번째 인수의 함수에 변수에 설정할 값의 반환 두번째 인수에 의존배열 전달
<Child1.jsx>
import { Child2 } from './Child2';
import { Child3 } from './Child3';
import { memo } from 'react';
const style = {
height : '200px',
backgroundColor : 'lightblue',
padding : '8px'
};
export const Child1 = memo((props) => {
console.log('Child1 렌더링');
// props로부터 함수를 전개(분할대입)
const {onClickReset} = props;
return (
<div style={style}>
<p>Child1</p>
{/* 전달된 함수를 실행하는 버튼 설정 */}
<button onClick={onClickReset}>리셋</button>
<Child2 />
<Child3 />
</div>
);
});
- const {onClickReset} = props; ➡️ App 컴포넌트의 onClickReset 함수를 Child1 컴포넌트의 props를 통해 받아온다.
문제 : 함수 정의도 props가 변경되지 않았는데 재렌더링 된다.
원인 : 함수를 props로 전달할 때 컴포넌트의 메모이제이션해도 재렌더링 됐던 이유는 - React에서는 함수가 새로 생성될 때마다 해당 함수를 사용하는 컴포넌트가 재렌더링되기 때문이다.
해결 : 함수 메모이제이션 - useCallback 사용한다.
✔︎ 함수의 메모이제이션 - useCallback
: 만들어놨던 함수 재활용을 통해 해당 함수의 참조가 변경되지 않음
<App.jsx>
import { useState, memo, useCallback } from "react";
import { Child1 } from "./components/Child1.jsx";
import { Child4 } from './components/Child4.jsx';
// import './App.css';
const App = memo(() => {
console.log("렌더링 함수")
const [num, setNum] = useState(0);
const onClickButton = () => {
setNum(num + 1);
};
/* 리셋 버튼을 누르면 Child1도 재렌더링
const onClickReset = () => {
setNum(0);
};
*/
// useCallback : Child1 재렌더링 x
const onClickReset = useCallback(() => {
setNum(0);
},[]);
return (
<>
<button onClick={onClickButton}>버튼</button>
<p>{num}</p>
{/* Child1 클릭하면 0으로 되돌리는 리셋버튼 구현 */}
{/* state는 App.js가 가지고 있으므로 app안에서 리셋하기 위한 함수 정의하고 함수를 child에 전달 */}
<Child1 onClickReset={onClickReset} />
<Child4 />
</>
);
});
export default App; // export를 해줘야 index.jsx에서 App.jsx를 받을 수 있음
onClickReset 함수를 useCallback으로 감싸는 이유 (?)
재렌더링 최적화: onClickReset 함수가 App 컴포넌트 내부에서 상태 변경에 영향을 받지 않고 고정된 역할만 수행하는 경우, 해당 함수를 메모이제이션하여 불필요한 재생성을 방지할 수 있다.
의존성 배열 제어: useCallback의 두 번째 매개변수([]=빈배열)인 의존성 배열을 사용하여 함수가 의존하는 변수가 변경될 때만 함수가 재생성되도록 제어하는 것이다. 빈배열의 경우 해당 함수가 생성될 때 상태만 기억하고 상태가 변경되어도 함수를 재생성하지 않는다! 이를 통해 필요한 경우에만 함수를 재생성할 수 있고 그렇지 않으면 이전에 메모이제이션된 함수를 사용할 수 있다.
만약, useCallback의 두 번째 매개변수를 [num]으로 받았다면 (?)
onClickReset 함수는 num 상태에 의존하게 되고 이 경우, num 상태가 변경될 때마다 onClickReset 함수가 새로 생성된다.
그러므로 Child1 컴포넌트의 재렌더링이 발생할 때마다 onClickReset 함수가 다시 생성되고, Child1 컴포넌트의 렌더링 로그가 콘솔창에 찍히게 된다.
번외인데요, npm start 했을 때
자꾸 타입에러가 나더라구요... 미치고 환장할 노릇... 뭐 때문인데 :(
import useState from "react"; (X)
알고보니 useState가 react 패키지의 기본 함수가 아니기 때문에 디폴트 임포트를 하면 안됐던 것이다... 반드시 네임드 임포트 해주세요
📌 출처
TypeError: react__WEBPACK_IMPORTED_MODULE_0___default is not a function or its return value is not iterable
I'm simply following this tutorial on youtube. useState is giving errors. Please help me here import React from 'react' //import useState from 'react-dom'; import useState from 'react'; export...
stackoverflow.com
'프로그래밍 > React' 카테고리의 다른 글
📌 State - UseState, UseRef (0) | 2024.03.30 |
---|---|
[React] Global State(전역 상태) (0) | 2024.03.21 |
[React CSS] 리액트에서 다양한 방법으로 CSS 적용하기 (0) | 2024.03.19 |
[React] 리액트 시작 (0) | 2024.03.19 |
[React] 리액트 기본 문법 (0) | 2024.03.19 |