Hook을 사용하여 모든 컴포넌트까지(함수형?) 독립적으로 재사용 가능하다.
useState: 상태를 사용함useEffect: 상태가 변화할때 실행됨.useRef: 변경 가능한 ref객체 생성useContext: 컨텍스트 값을 사용함useMemo : 상태가 변화할 때 계산useCallback : 함수가 변화할 때 재정의관리할 상태를 초기화
ex. CountComponent.js
import React, { useState } from "react";
export default function CountComponent() {
const [count, setCount] = useState(0)
const addCount = () => {
setCount(count+1);
}
return (
<div>
<div>{count}</div>
<button onClick={addCount}>1 증가</button>
</div>
)
}```
컴포넌트가 mount(초기 Rendering) 되거나
업데이트 될 때 (State, props 등이 변경시) 실행할 함수 정의 (보통 서버에 데이터 요청을 처리함)
ex. CountComponent.js
import React, { useEffect, useState } from "react";
export default function CountComponent() {
const [count, setCount] = useState(0)
const addCount = () => {
setCount(count+1);
}
useEffect(() => {
console.log("데이터받아오기! 이 함수는 한번만 실행됩니다.");
return () => {
console.log("메모리를 잡아먹으면 리소스를 해제하는 함수를 return해주어야 합니다.")
}
}, [])
// count가 바뀔때마다 다시 호출할게용
useEffect(() => {
console.log(`카운트가 증가할 때마다 실행 \n -count: ${count}`)
}, [count])
return (
<div>
<div>{count}</div>
<button onClick={addCount}>1 증가</button>
</div>
)
}
App.js
import logo from './logo.svg';
import './App.css';
import HelloWorld from './components/HelloWorld';
import CaptionImage from './components/CaptionImage';
import BlinkCompnent from './components/BlinkComponent.js';
import CountComponent from './components/CountComponent.js';
import { useState } from 'react';
function App() {
const [visible, setVisible] = useState(false)
return (
<div className="App">
{/* visible 상태에 따라서 CountComponent 보였다 안보였다 */}
<button onClick={() => setVisible(!visible)}>보이기</button>
{visible? <CountComponent/> : null}
</div>
);
}
export default App;
다른 컴포넌트를 참조하여햐 할 때
RegisterInputButton.js
import React, {useEffect, useRef, useState} from 'react'
export default function FocusInputButton({text}) {
const inputRef = useRef();
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
<button onClick={focusInput}> 입력하러 가기 </button>
<div style={{height:2000}}></div>
<input ref={inputRef} type="text"/>
<div style={{height:2000}}></div>
</div>
)
}
App.js
import logo from './logo.svg';
import './App.css';
import HelloWorld from './components/HelloWorld';
import CaptionImage from './components/CaptionImage';
import BlinkCompnent from './components/BlinkComponent.js';
import CountComponent from './components/CountComponent.js';
import { useState } from 'react';
import FocusInputButton from './components/RegisterInputButton.js';
function App() {
const [visible, setVisible] = useState(false)
return (
<div className="App">
{/* visible 상태에 따라서 CountComponent 보였다 안보였다 */}
{/* <button onClick={() => setVisible(!visible)}>보이기</button>
{visible? <CountComponent/> : null} */}
<FocusInputButton/>
</div>
);
}
export default App;
값을 메모이제이션 (잠깐 저장)
useMemo 사용하지 않았을 때
PrimeCalculator.jsimport React, {useState, useMe} from "react";
function calculatePrimes(limit) {
console.log(`limit: ${limit}에 대한 소수 계산`)
const primes = [];
for(let i=2; i<=limit; i++) {
let isPrime = true;
for(let j=2; j<i; j++) {
if (i % j == 0) {
isPrime = false;
break;
}
}
if (isPrime) {
primes.push(i);
}
}
return primes;
}
export default function PrimeCalculator(props) {
const [limit, setLimit] = useState(10);
const primes = calculatePrimes(limit);
return (
<div>
<input type="number" value={limit} onChange={(e) => setLimit(Number(e.target.value))}/>
<p>계산된 소수 : {primes.join(', ')}</p>
</div>
)
}
useMemo 사용하였을 때
10000할때도 멈추지 않음
PrimeCalculator.jsimport React, { useState, useMemo } from "react";
function calculatePrimes(limit) {
console.log(`limit: ${limit}에 대한 소수 계산`)
const primes = [];
for(let i=2; i<=limit; i++) {
let isPrime = true;
for(let j=2; j<i; j++) {
if (i % j == 0) {
isPrime = false;
break;
}
}
if (isPrime) {
primes.push(i);
}
}
return primes;
}
export default function PrimeCalculator(props) {
const [limit, setLimit] = useState(10);
// const primes = calculatePrimes(limit);
const primes = useMemo(() => calculatePrimes(limit), [limit]);
return (
<div>
<input
type="number"
value={limit}
onChange={(e) => setLimit(Number(e.target.value))}/>
<p>계산된 소수 : {primes.join(', ')}</p>
</div>
)
}
함수를 정의하는 것도 비용이다
함수 정의도 dependency가 변경될 때에만 재정의하기
useCallback 사용하지 않았을 때
const [count, setCount] = useState(0)
const addCount = () => {
setCount(count+1);
}
useCallback 사용하였을 때
const addCount = useCallback(() => {
setCount(count);
}, [count])

🤖 GPT Say..
useCallback 훅을 사용하면 리액트 컴포넌트가 렌더링될 때마다 함수가 새로 생성되는 것을 방지할 수 있습니다.
함수가 렌더링 될 때마다 새로 생성되는 경우, 해당 함수를 포함한 컴포넌트나 하위 컴포넌트가 리렌더링되는데, 이는 불필요한 성능 손실을 초래할 수 있습니다.addCount 함수를 살펴봅시다.
useCallback을 사용하지 않으면, 함수 내부에서 count 변수에 직접 접근하여 상태를 변경하는데, 이 때마다 addCount 함수가 렌더링 될 때마다 새로 생성됩니다.
그리고 새로 생성된 함수는 새로운 레퍼런스를 가지고 있기 때문에, 이 함수를 참조하는 곳들은 새 함수가 생성되었다는 사실을 인지하고 다시 렌더링됩니다.하지만 useCallback을 사용하면, 의존성 배열에 명시된 값들이 변하지 않는 한, 해당 함수는 렌더링 될 때마다 새로 생성되지 않습니다. 따라서 addCount 함수가 여러 번 렌더링될 때마다 동일한 함수 레퍼런스를 반환하므로, 함수를 참조하는 곳들이 불필요하게 다시 렌더링되지 않게 됩니다.
결론적으로, useCallback을 사용하면 함수를 새로 생성하는 불필요한 오버헤드를 방지하여 성능을 향상시킬 수 있습니다.
🤖 GPT Say..
useCallback과useMemo는 모두 리액트 훅이며, 둘 다 성능 최적화를 위해 사용됩니다.
그러나 각각의 주요 목적과 사용 사례에는 차이가 있습니다.
useCallback:
- 주로 콜백 함수를 메모이제이션(memoization)하는 데 사용됩니다.
- 함수를 새로 만들지 않고 이전에 생성된 함수를 재사용할 수 있도록 합니다.
- 의존성 배열을 통해 콜백 함수가 의존하는 값들을 명시하여, 해당 값들이 변경되지 않으면 같은 함수를 반환하도록 제어할 수 있습니다.
- 이전에 생성된 함수를 재사용함으로써 렌더링 성능을 향상시킬 수 있습니다.
useMemo:
- 주로 값을 메모이제이션(memoization)하는 데 사용됩니다.
- 계산 비용이 높은 연산의 결과를 캐시하여, 이전에 계산된 값이 필요할 때 재사용할 수 있도록 합니다.
- 의존성 배열을 통해 값이 변경되지 않으면 캐시된 값을 유지하고, 변경되면 재계산합니다.
- 주로 계산 비용이 높은 연산의 결과를 캐시하여 성능을 향상시키는 데 사용됩니다.
간단하게 말하면, useCallback은 함수를, useMemo는 값을 메모이제이션하는 데 사용됩니다.
useCallback은 콜백 함수의 생성을 최적화하는 데 사용되고, useMemo는 값을 캐싱하여 계산 비용을 줄이는 데 사용됩니다.
컴포넌트 Tree 내에서 데이터를 상호간에 공유할 때 사용하는 훅
(너무 많이 사용하는 것은 관리 비용이 증가할 수 있음)
ThemeProvider.js


MyPage.js

ThemeButton.js

App.js
import logo from './logo.svg';
import './App.css';
import { useState } from 'react';
import TodoList from './components/TodoList.js';
import { ThemeProvider } from './components/ThemeProvide.js';
import ThemeButton from './components/ThemeButton.js';
import MyPage from './components/MyPage.js';
function App() {
const [visible, setVisible] = useState(false)
return (
<ThemeProvider>
<ThemeButton/>
<MyPage/>
</ThemeProvider>
);
}
export default App;