목차
React Component Tree
리액트의 컴포넌트 트리는 다음과 같이 만들어 진다.
App을 중심으로 순차적으로 만들어 진다.
React Dev Tools
이것을 이용하면 여러 분석으로 할 수 있다.
아래와 같은 웹에서 increment 버튼을 눌렀을 때 렌더링을 분석할 수 있다.
컴포넌트 랜더링 이유 분석 방법
memo를 통한 랜더링 최적화 하기
아래와 같은 코드가 있을 때 첫번째 상태인 enteredNum만 바꾸었는데 모든 컴포넌트가 리랜더링이 일어난다.
따라서 이것을 막기위해서 memo를 사용한다.
import { useState } from 'react';
import Counter from './components/Counter/Counter.jsx';
import Header from './components/Header.jsx';
import { log } from './log.js';
function MainApp() {
log('<App /> rendered');
const [enteredNumber, setEnteredNumber] = useState(0);
const [chosenCount, setChosenCount] = useState(0);
function handleChange(event) {
setEnteredNumber(+event.target.value);
}
function handleSetClick() {
setChosenCount(enteredNumber);
setEnteredNumber(0);
}
return (
<>
<Header />
<main>
<section id="configure-counter">
<h2>Set Counter</h2>
<input type="number" onChange={handleChange} value={enteredNumber} />
<button onClick={handleSetClick}>Set</button>
</section>
<Counter initialCount={chosenCount} />
</main>
</>
);
}
export default MainApp;
이런식으로 Counter함수를 memo로 묶게되면 App Component에서 전달하는 속성인 initialCount의 변화여부만 확인하여
변하지 않았으면 재랜더링 하지 않는다.
import { useState, memo } from "react";
const Counter = memo(function Counter({ initialCount }) {
...
return (
<section className="counter">
<p className="counter-info">
The initial counter value was <strong>{initialCount}</strong>. It{" "}
<strong>is {initialCountIsPrime ? "a" : "not a"}</strong> prime number.
</p>
<p>
<IconButton icon={MinusIcon} onClick={handleDecrement}>
Decrement
</IconButton>
<CounterOutput value={counter} />
<IconButton icon={PlusIcon} onClick={handleIncrement}>
Increment
</IconButton>
</p>
</section>
);
});
export default Counter;
React 구조를 바꾸어 랜더링 최적화하기
import { useState } from 'react';
import Counter from './components/Counter/Counter.jsx';
import Header from './components/Header.jsx';
import { log } from './log.js';
function MainApp() {
log('<App /> rendered');
const [enteredNumber, setEnteredNumber] = useState(0);
const [chosenCount, setChosenCount] = useState(0);
function handleChange(event) {
setEnteredNumber(+event.target.value);
}
function handleSetClick() {
setChosenCount(enteredNumber);
setEnteredNumber(0);
}
return (
<>
<Header />
<main>
<section id="configure-counter">
<h2>Set Counter</h2>
<input type="number" onChange={handleChange} value={enteredNumber} />
<button onClick={handleSetClick}>Set</button>
</section>
<Counter initialCount={chosenCount} />
</main>
</>
);
}
export default MainApp;
아래와 같이 컴포너트를 분리해 상태를 내려주어 MainApp컴포넌트의 리랜더링을 막아준다.
import { useState } from 'react';
import ConfigureCounter from './components/Counter/ConfiguerCounter.jsx';
import Counter from './components/Counter/Counter.jsx';
import Header from './components/Header.jsx';
import { log } from './log.js';
function MainApp() {
log('<App /> rendered');
const [chosenCount, setChosenCount] = useState(0);
function handleSetCount(newCount){
setChosenCount(newCount);
}
return (
<>
<Header />
<main>
<ConfigureCounter setChosenCount={handleSetCount}></ConfigureCounter>
<Counter initialCount={chosenCount} />
</main>
</>
);
}
export default MainApp;
import { useState } from "react";
import { log } from "../../log";
function ConfigureCounter({ setChosenCount }) {
log("<ConfigureCouner/>", 1);
const [enteredNumber, setEnteredNumber] = useState(0);
function handleChange(event) {
setEnteredNumber(+event.target.value);
}
function handleSetClick() {
setChosenCount(enteredNumber);
setEnteredNumber(0);
}
return (
<section id="configure-counter">
<h2>Set Counter</h2>
<input type="number" onChange={handleChange} value={enteredNumber} />
<button onClick={handleSetClick}>Set</button>
</section>
);
}
export default ConfigureCounter;
useMemo VS Memo Hook
useMemo
계산된 값을 메모이제이션하여 불필요한 계산을 피한다.
import React, { useMemo } from 'react';
function ExpensiveComponent({ a, b }) {
// useMemo를 사용하여 계산된 값을 메모이제이션
const expensiveValue = useMemo(() => {
return a + b; // 가정: 이 계산은 매우 비용이 많이 드는 작업임
}, [a, b]);
return (
<div>
{expensiveValue}
</div>
);
}
Memo Hook
컴포넌트 자체를 메모이제이션하여 props가 변경되지 않는 한 재렌더링을 방지한다.
import React, { memo } from 'react';
const MyComponent = ({ name }) => {
console.log('MyComponent rendered');
return (
<div>
Hello, {name}!
</div>
);
};
// memo를 사용하여 컴포넌트를 메모이제이션
export default memo(MyComponent);
key의 중요성
상태는 컴포넌트 타입에만 속한 게 아니라 컴포넌트가 사용되는 위치에도 속해 있다.
아래 예를 통해 알아보자
import { useState } from 'react';
import { log } from '../../log.js';
function HistoryItem({ count }) {
log('<HistoryItem /> rendered', 3);
const [selected, setSelected] = useState(false);
function handleClick() {
setSelected((prevSelected) => !prevSelected);
}
return (
<li onClick={handleClick} className={selected ? 'selected' : undefined}>
{count}
</li>
);
}
//index는 엄격하게 매핑되는 값이 아니라 항상 정적으로 유지된다.
export default function CounterHistory({ history }) {
log('<CounterHistory /> rendered', 2);
return (
<ol>
{history.map((count, index) => (
<HistoryItem key={index} count={count} />
))}
</ol>
);
}
아래 코드는 일부지만 실행시켜보면 다음과 같다.
따라서 아래와 같이 정적인 위치의 컴포넌트를 만들때는 신경 쓸 필요가 없는 것과 달리
컴포넌트의 위치가 동적으로 변할 때는 key를 신경써주어야 한다.
import { useState } from 'react';
import ConfigureCounter from './components/Counter/ConfiguerCounter.jsx';
import Counter from './components/Counter/Counter.jsx';
import Header from './components/Header.jsx';
import { log } from './log.js';
function MainApp() {
return (
<>
<Header />
<main>
<Counter initialCount={chosenCount} />
<Counter initialCount={0} />
</main>
</>
);
}
export default MainApp;
unique한 속성으로 key 전달해 준 수정된 코드
import { useState } from 'react';
import { log } from '../../log.js';
function HistoryItem({ count }) {
log('<HistoryItem /> rendered', 3);
const [selected, setSelected] = useState(false);
function handleClick() {
setSelected((prevSelected) => !prevSelected);
}
return (
<li onClick={handleClick} className={selected ? 'selected' : undefined}>
{count}
</li>
);
}
export default function CounterHistory({ history }) {
log('<CounterHistory /> rendered', 2);
return (
<ol>
{history.map((count) => (
<HistoryItem key={count.id} count={count.value} />
))}
</ol>
);
}
key를 써야하는 또 다른 이유
unique한 키를 통해 해야 업데이트를 추가한 요소만 하는 것을 볼 수 있다.
MillionJs를 통한 랜더링 최적화
npm install million
https://pyjun01.github.io/v/million-js/
'코딩 정보 > React' 카테고리의 다른 글
[React] Custom Hook에 대해 알아보자 (0) | 2024.07.31 |
---|---|
[React] Http 통신을 해보자 (0) | 2024.07.30 |
[React] useEffect와 관련해 더 개념을 알아보자 (0) | 2024.07.23 |
[React] sideEffect가 무엇이고 useEffect에 대해 알아보자 (0) | 2024.07.19 |
[React] 랜더링의 동작과 관련 개념을 자세히 알아보자 (1) | 2024.07.17 |