React Hooks
- useState
- useEffect
- useRef
- useContext
- React.memo
- useCallback
- useMemo
useState
는 React
에서 함수형 컴포넌트의 상태를 관리하기 위한 기본적인 Hook
이다.
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
useState
는 state
값이 변경될때마다 리렌더링된다.useEffect
는 함수형 컴포넌트에서 부수 효과(side effects)를 처리하기 위해 사용된다. 데이터 가져오기, DOM 업데이트, 이벤트 구독/해제 등 부수 효과를 필요로 하는 작업을 처리할 때 유용하다.
import React, { useState, useEffect } from 'react';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setSeconds(prevSeconds => prevSeconds + 1);
}, 1000);
return () => clearInterval(interval); // 컴포넌트가 언마운트될 때 정리(cleanup)
}, []); // 빈 배열을 의존성으로 전달하여 마운트 시 한 번만 실행
return <div>Seconds: {seconds}</div>;
}
의존성 배열을 빈 배열로 전달하면 해당 부수 효과는 컴포넌트가 처음 마운트될 때 한 번만 실행된다.
의존성 배열을 생략하면 컴포넌트가 리렌더링될 때마다 부수 효과가 실행된다.
특정 상태나 값이 변경될 때만 부수 효과를 실행하려면 의존성 배열에 해당 상태를 추가하면 된다.
useRef
는 함수형 컴포넌트에서 참조를 생성하는데 사용된다.
import React, { useRef } from 'react';
function FocusInput() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus(); // input 요소에 직접 포커스를 설정
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus Input</button>
</div>
);
}
useRef
는 DOM
요소에 접근할 뿐 아니라, 컴포넌트의 상태로 관리할 필요는 없지만 값이 리렌더링 간 유지되어야 할 때 사용된다.
컴포넌트가 리렌더링될 때 useRef
의 값은 유지되며, 이를 통해 값이 리셋되지 않도록 할 수 있다.
useContext
는 React
의 Context API
와 함께 사용되어, 컴포넌트 트리 전체에 걸쳐 전역 상태를 쉽게 공유할 수 있게 한다. props drilling
피하고 싶을 때 유용하다.
Context
를 생성하고, Provider
를 통해 데이터를 공급한다.import React, { createContext, useContext, useState } from 'react';
const ThemeContext = createContext();
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
return (
<div>
<ThemeButton />
</div>
);
}
function ThemeButton() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme (Current: {theme})
</button>
);
}
props drilling
을 피할때 사용하면 좋지만 리렌더링 성능에 영향을 줄 수 있으므로 필요한 경우에만 사용하고, 상태 분리 및 최적화가 필요할 수 있다.React.memo
는 props
가 변경되지 않으면 해당 컴포넌트를 다시 렌더링하지 않도록 해준다.
import React from 'react';
const ChildComponent = React.memo(({ value }) => {
console.log("ChildComponent rendered");
return <div>{value}</div>;
});
function ParentComponent() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment</button>
<ChildComponent value="Hello" />
</div>
);
}
import "./TodoItem.css";
import { memo } from "react";
const TodoItem = ({ id, isDone, content, date, onUpdate, onDelete }) => {
const onChangeCheckbox = () => {
onUpdate(id);
};
const onClickDeleteBtn = () => {
onDelete(id);
};
return (
<div className="TodoItem">
<input
onChange={onChangeCheckbox}
readOnly
checked={isDone}
type="checkbox"
/>
<div className="content">{content}</div>
<div className="date">{new Date(date).toLocaleDateString()}</div>
<button onClick={onClickDeleteBtn}>제거</button>
</div>
);
};
export default memo(TodoItem);
React.memo
는 위 예제와 같이 사용될 수도 있다.useCallback
은 함수형 컴포넌트 내에서 함수를 메모이제이션하여, 함수가 불필요하게 다시 생성되지 않도록 하는 Hook
이다.
import React, { useState, useCallback } from 'react';
function ParentComponent() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
const increment = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []); // 의존성이 없으므로 처음 생성된 함수를 재사용
return (
<div>
<button onClick={increment}>Increment</button>
<ChildComponent onClick={increment} />
</div>
);
}
const ChildComponent = React.memo(({ onClick }) => {
console.log("ChildComponent rendered");
return <button onClick={onClick}>Child Increment</button>;
});
useCallback
을 사용하지 않으면, state
가 변경될때마다 부모컴포넌트가 리렌더링되어 함수도 같이 재생성된다.
하지만 위와 같이 useCallback
을 사용하면
함수가 메모이제이션이 되기 때문에 재생성되지 않는다.
React.memo
는 props
가 변경되지 않는 한 자식 컴포넌트를 리렌더링하지 않지만, 함수가 새로운 인스턴스로 생성되면 새로운 props
로 간주되어 리렌더링이 발생할 수 있다.
따라서, useCallback
을 사용해 함수 인스턴스를 고정하면, 자식 컴포넌트가 불필요하게 리렌더링되지 않도록 할 수 있다.
useMemo
는 특정 계산 작업을 메모이제이션하여 불필요한 계산을 방지하는 Hook
이다.
import React, { useState, useMemo } from 'react';
function ExpensiveComponent({ number }) {
const expensiveCalculation = (num) => {
console.log("Calculating...");
for (let i = 0; i < 1000000000; i++) {} // 오래 걸리는 작업
return num * 2;
};
const result = useMemo(() => expensiveCalculation(number), [number]);
return <div>Result: {result}</div>;
}
function App() {
const [count, setCount] = useState(0);
const [number, setNumber] = useState(1);
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment Count</button>
<button onClick={() => setNumber(number + 1)}>Increment Number</button>
<ExpensiveComponent number={number} />
</div>
);
}
expensiveCalculation
은 오래 걸리는 계산 작업을 수행하는 함수다.useMemo
를 사용해 number
가 변경될 때만 해당 계산을 실행하도록 설정했고, 이렇게 하면 count
가 변경되어도 ExpensiveComponent
가 재렌더링되더라도 expensiveCalculation
함수는 다시 실행되지 않는다.챌린지반 수업을 듣고나면 항상 멘탈이 갈린다.
나만 그런줄 알았는데, 대부분의 수강생들이 그런것같아 다행이다.
지금은 따라가느라 바쁘지만 나중에 뒤돌아봤을때
맞아 그 땐 그랬지... 하며 기분좋게 회상 할 수 있다면 좋겠다.
내일은 redux, react router dom, supabase에 대하여 공부해보자.