menu,main)가 있다.리액트는 컴포넌트 트리를 생성한다.

해당 탭의 메뉴에서 불꽃 모양 버튼 → 결과를 보일때 컴포넌트 함수들이 실행된 순서가 포함되어있다. 또한 컴포넌트 함수들과의 관계도도 볼 수 있다. - 이 프로젝트의 App이나 Header에 마우스를 올리면 렌더링이 되지 않았다고 나온다. → App, Header 컴포넌트 함수들이 다시 실행되지 않았기 때문이다. - Counter 컴포넌트는 렌더링 되었다. → 버튼을 클릭하여 재실행하였기 때문이다. - 컴포넌트 재평가(재실행)이 일어나면 전파하지 않는다. 그래서 리액트가 Counter 컴포넌트 함수를 재실행하는 것은 부모 컴포넌트인 App 컴포넌트에 아무런 영향을 주지 않는다.
컴포넌트 재실행 : 부모 컴포넌트에는 영향이 없지만 자식 컴포넌트에는 영향을 준다
해당 탭의 메뉴에서 Rank 버튼 → 렌더링된 컴포넌트들만 볼 수 있다.

memo()로 컴포넌트 함수 실행 방지하기memo : 리액트는 내장함수를 지원하기 때문에 이를 이용해 컴포넌트 함수들을 감쌀 수 있다. memo의 경우 불필요한 컴포넌트 함수 실행을 방지한다.memo가 이전 속성 값과 새로받을 속성값을 살펴본다. 만약 컴포넌트 함수가 실행됐을 대 속성 값들이 완전히 동일하다면(배열과 객체가 메모리 내의 배열, 객체와 동일하다) 해당 컴포넌트 함수 실행을 memo가 저지한다.memo는 오직 부모 컴포넌트에 의해 함수가 실행되었을 때만 이를 저지한다. → 컴포넌트의 속성이 변경되지 않았다면 부모 컴포넌트가 실행되도 해당 컴포넌트가 재실행될 이유가 없기 때문이다.import { memo } from "react";
const Counter = memo(function Counter({ prop }) {});
export default Couter;
memo를 가지고 모든 컴포넌트를 감싸서는 안된다.memo를 가지고 속성을 체크하는 것은 성능에 영향을 준다!memo보다 더 강력한 방법. → 컴포넌트 분리하기memo를 없애야 한다!import { useState } from "react";
import { log } from "../../log";
export default function ConfigureCounter({ onSet }) {
log("<ConfigureCounter />");
const [enteredNumber, setEnteredNumber] = useState(0);
function handleChange(event) {
setEnteredNumber(+event.target.value);
}
function handleSetClick() {
onSet(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>
);
}
import { useState } from "react";
import Counter from "./components/Counter/Counter.jsx";
import Header from "./components/Header.jsx";
import ConfigureCounter from "./components/Counter/ConfigureCounter.jsx";
import { log } from "./log.js";
function App() {
log("<App /> rendered");
const [chosenCount, setChosenCount] = useState(0);
function handleSetCount(newCount) {
setChosenCount(newCount);
}
return (
<>
<Header />
<main>
<ConfigureCounter onSet={handleSetCount} />
<Counter initialCount={chosenCount} />
</main>
</>
);
}
export default App;

useCallback() 훅 이해하기children, icon, ...props가 있다.children은 텍스트이고 이 값은 변하지 않는다.icon 속성은 Counter 속성에서 포인터(pointer, ex. MinusIcon, PlusIcon)를 받는다. 컴포넌트의 이름으로만 전달하고 IconButton.jsx에 와서야 JSX코드로 바뀌게 된다. → 포인터(함수들의 이름)들은 바뀌지 않는 성질을 갖는다....props에는 나머지 속성들을 가지는데, Counter 컴포넌트에서 IconButton에게 onClick 속성을 전달한다.useCallback이다.// IconButton.jsx -> memo 사용
import { memo } from "react";
import { log } from "../../log.js";
const IconButton = memo(function IconButton({ children, icon, ...props }) {
log("<IconButton /> rendered", 2);
const Icon = icon;
return (
<button {...props} className="button">
<Icon className="button-icon" />
<span className="button-text">{children}</span>
</button>
);
});
export default IconButton;
// Counter.jsx
import { useState, memo, useCallback } from "react";
import IconButton from "../UI/IconButton.jsx";
import MinusIcon from "../UI/Icons/MinusIcon.jsx";
import PlusIcon from "../UI/Icons/PlusIcon.jsx";
import CounterOutput from "./CounterOutput.jsx";
import { log } from "../../log.js";
function isPrime(number) {
log("Calculating if is prime number", 2, "other");
if (number <= 1) {
return false;
}
const limit = Math.sqrt(number);
for (let i = 2; i <= limit; i++) {
if (number % i === 0) {
return false;
}
}
return true;
}
export default function Counter({ initialCount }) {
log("<Counter /> rendered", 1);
const initialCountIsPrime = isPrime(initialCount);
const [counter, setCounter] = useState(initialCount);
const handleDecrement = useCallback(function handleDecrement() {
setCounter((prevCounter) => prevCounter - 1);
}, []);
const handleIncrement = useCallback(function handleIncrement() {
setCounter((prevCounter) => prevCounter + 1);
}, []);
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>
);
}
useCallback : 함수의 재생성 방지를 위해 사용.
Increment, Decrement 버튼을 눌러도 불필요하게 IconButton이 재실행되지 않는다.
useMemo() 훅 이해하기Counter.jsx의 isPrime()함수가 Increment, Decrement 버튼을 누를 때마다 재실행되는 것을 알 수 있다.(Calculation if is prime number log로 확인 가능.)
initialCount 속성을 입력값으로 받고 그에 대한 리턴값을 제공한다.useMemo훅을 이용해 불필요한 일반 함수의 재실행도 방지
memo는 컴포넌트 함수를 감싸는데 사용하고,useMemo는 컴포넌트 안에 있는 일반 함수들을 감싸고 그들의 실행을 방지한다. 이useMemo는 복잡한 계산이 있을 때만 사용해야 한다.
import { useState, memo, useCallback, useMemo } from "react";
import IconButton from "../UI/IconButton.jsx";
import MinusIcon from "../UI/Icons/MinusIcon.jsx";
import PlusIcon from "../UI/Icons/PlusIcon.jsx";
import CounterOutput from "./CounterOutput.jsx";
import { log } from "../../log.js";
function isPrime(number) {
log("Calculating if is prime number", 2, "other");
if (number <= 1) {
return false;
}
const limit = Math.sqrt(number);
for (let i = 2; i <= limit; i++) {
if (number % i === 0) {
return false;
}
}
return true;
}
const Counter = memo(function Counter({ initialCount }) {
log("<Counter /> rendered", 1);
const initialCountIsPrime = useMemo(
() => isPrime(initialCount),
[initialCount]
);
// 의존성이 없다면 다시 재실행하지 않음(바뀔 수 있는 의존성이 없으니까)
// 여기서는 initialPrime이 바뀌면 해당 Memo함수가 실행되니까 의존성에 넣어줘야 한다.
const [counter, setCounter] = useState(initialCount);
const handleDecrement = useCallback(function handleDecrement() {
setCounter((prevCounter) => prevCounter - 1);
}, []);
const handleIncrement = useCallback(function handleIncrement() {
setCounter((prevCounter) => prevCounter + 1);
}, []);
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;

useMemo를 너무 남용해선 안된다. memo처럼 의존성 값 비교를 계속해서 수행하기 때문이다!리액트는 트리에서 변경된 부분을 파악하고 실행된 컴포넌트 함수들만 찾는다. 그리고 업데이트된 HTML코드를 전달 → 이전 가상 DOM과 비교 → 변동사항들을 실제 DOM에 적용한다.

// CounterHistory.jsx
export default function CounterHistory({ history }) {
log("<CounterHistory /> rendered", 2);
return (
<ol>
{history.map((count, index) => (
<HistoryItem key={index} count={count} />
))}
</ol>
);
}
// Counter.jsx
const Counter = memo(function Counter({ initialCount }) {
log("<Counter /> rendered", 1);
const initialCountIsPrime = useMemo(
() => isPrime(initialCount),
[initialCount]
);
const [counterChanges, setCounterChanges] = useState([
{ value: initialCount, id: Math.random() * 1000 }, // value, id 값 설정
]);
const currentCounter = counterChanges.reduce(
(prevCounter, counterChanges) => prevCounter + counterChanges.value,
0
);
const handleDecrement = useCallback(function handleDecrement() {
// setCounter((prevCounter) => prevCounter - 1);
setCounterChanges((prevCounterChanges) => [
{ value: -1, id: Math.random() * 1000 },
...prevCounterChanges,
]);
}, []);
const handleIncrement = useCallback(function handleIncrement() {
// setCounter((prevCounter) => prevCounter + 1);
setCounterChanges((prevCounterChanges) => [
{ value: 1, id: Math.random() * 1000 },
...prevCounterChanges,
]);
}, []);
});
export default Counter;
// CounterHistory.jsx
export default function CounterHistory({ history }) {
log("<CounterHistory /> rendered", 2);
return (
<ol>
{history.map((count) => (
{/* key값이 id 전달 */}
<HistoryItem key={count.id} count={count.value} />
))}
</ol>
);
}

key : 위의 예시처럼 state가 건너뛰는 것을 방지하는 역할을 한다. 또한 개발자 창의 Elements에서 보면 count.id를 사용하여 키를 설정했을 때 해당 목록(ol)의 부분(li)만 업데이트 된다. 즉, li 첫번째 요소만 업데이트 된다. → 이전 DOM 요소들을 재사용하게 된다.function App(){
return(
<main>
<Counter key={chosenCount} initialCount={chosenCount} />
</main>
)
}

setUpdate(prevState => !prevState) 형식의 함수 형태를 사용한다.npx million@latestimport million from "million/compiler";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [million.vite({ auto: true }), react()],
});