✏️ 들어가기 전에
내가 프로젝트를 진행하면서 사용했던 React Hooks는 useEffect, useState, useRef 밖에 없다. React 공식문서를 보니까 정말정말 다양한 훅들이 많더라.. 하나하나 보면서 얼마나 다양한 기능들이 있을지 알아보자!!
* 모든 내용은 React 공식 문서를 참고하여 작성하였습니다. *
💡 시작하기
Hooks를 사용하면 컴포넌트에서 다양한 React 기능을 사용할 수 있다.
내장된 Hooks를 사용하거나 Hooks를 결합하여 직접 빌드할 수 있다.
예전 버전에는 클래스형 컴포넌트를 많이 사용하였다.
클래스형 컴포넌트에는 고유 기능인 상태 관리와 라이플사이클 관리 기능을 가지고 있기 때문이다.
하지만 hooks가 등장하면서 함수형 컴포넌트에서도 이 기능들을 사용할 수 있게 해주었다.
✅ Hooks의 규칙
- 컴포넌트의 최상위 레벨이나 자신의 hook에서만 호출할 수 있으며, 루프나 조건문 내에서 호출할 수 없다.
⚙️ Hooks의 종류
🪝State Hooks
State는 컴포넌트가 사용자의 입력과 같은 정보를 기억한다.
ex) form component : state를 사용하여 입력값 저장 image gallery component : state를 사용하여 선택한 이미지의 인덱스 저장
useState
: 직접 업데이트할 수 있는 state 변수를 선언한다.
const [name, setName] = useState('sunwoo')
const [age. setAge] = useState(22)
const [form, setForm] = useState({
firstName: 'Barbara',
lastName: 'Hepworth',
email: 'bhepworth@sculpture.com',
});
- name : 상태 변수의 이름
- setName : state를 다른 값으로 변경할 수 있는 함수
- 이는 다음 useState 렌더링 때 적용됨
- updater function : 이전 상태를 기반으로 상태를 업데이트함
setName('hihello') setName(prev => prev + 1) // updater function
- 상태의 객체 및 배열 업데이트 : spread 연산자 사용
- setForm({ ...form, firstName: 'Taylor' });
- ‘sunwoo’ : state의 초기값
useReducer
: 컴포넌트에 reducer를 추가할 수 있는 react hook
function reducer(state, action) {
// ...
}
function MyComponent() {
const [state, dispatch] = useReducer(reducer, { age: 42 });
// ...
- Reducer 함수를 따로 작성해야 한다. (보통 switch문을 많이 사용)
- function reducer(state, action) { switch (action.type) { case 'incremented_age': { return { name: state.name, age: state.age + 1 }; } case 'changed_name': { return { name: action.nextName, age: state.age }; } } throw Error('Unknown action: ' + action.type); }
- reducer : state가 어떻게 업데이트되는지 지정하는 reducer 함수
- {age : 42} : 초기 state
- dispatch : state를 다른 값으로 변경할 수 있는 함수
-
- action : 사용자가 수행한 작업
function reducer(state, action) { if (action.type === 'incremented_age') { return { age: state.age + 1 }; } throw Error('Unknown action.'); } . . <button onClick={() => { dispatch({ type: 'incremented_age' }) }}> Increment age </button> // action.type = incremented_age
-
useState와 useReducer는 매우 유사하지만, useReducer는 이벤트 핸들러에서 state를 업데이트하는 로직을 컴포넌트 외부의 single function로 옮길 수 있다.
🪝Context Hooks
Context를 사용하면 컴포넌트에 props로 전달하기 않고도 정보를 받을 수 있다.
useContext
: 컴포넌트에서 context를 읽고 구독할 수 있게 해주는 react hook
import { useContext } from 'react';
function MyComponent() {
const theme = useContext(ThemeContext);
// ...
- ThemeContext : 이전에 createContext를 통해 만든 context
- theme : context value
context를 button에 전달하려면 Provider로 감싸면 된다.
function MyPage() {
return (
<ThemeContext.Provider value="dark">
<Form />
</ThemeContext.Provider>
);
}
function Form() {
// ... renders buttons inside ...
}
Context의 업데이트를 원한다면, state와 함께 사용하면 된다
function MyPage() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={theme}> // value : state
<Form />
<Button onClick={() => {
setTheme('light');
}}>
Switch to light theme
</Button>
</ThemeContext.Provider>
);
}
🪝Ref Hooks
컴포넌트가 렌더링에 사용되지 않는 정보를 보유하도록 한다. state와 달리 reference를 업데이트해도 컴포넌트가 다시 렌더링되지 않는다.
useRef
: 렌더링이 필요하지 않은 값을 참조할 수 있게 해줌
import { useRef } from 'react';
function MyComponent() {
const intervalRef = useRef(0);
const inputRef = useRef(null);
// ...
- intervalRef, inputRef : 참조 객체 반환
- ref 사용 시 장점
- 리렌더링하는 사이 정보 저장 가능
- ref 값 변경해도 다시 렌더링이 발생하지 않음!!
useImperativeHandle
: ref로 노출되는 handle을 사용자 정의할 수 있음
import { forwardRef, useImperativeHandle } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
useImperativeHandle(ref, () => {
return {
// ... your methods ...
};
}, []);
// ...
- createHandle : 인수를 취하지 않고 노출하려는 ref handle을 반환하는 함수
- forwardRef : 상위 컴포넌트에서 하위 컴포넌트로 ref를 전달함
🪝Effect Hooks
useEffect
: 컴포넌트를 외부 시스템과 동기적으로 작동하게 함
import { useEffect } from 'react';
import { createConnection } from './chat.js';
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('<https://localhost:1234>');
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [serverUrl, roomId]);
// ...
}
- setup code : 외부 시스템과 연결하는 코드
- cleanup code : 실행할 함수
- list of dependencies : 업데이트 시 return 내의 함수 발생시킴
useLayoutEffect
: useEffect의 변형 hook, 브라우저가 화면을 다시 그리기 전에 발생
- 레이아웃 측정 가능
useInsertionEffect
: useEffect의 변형 hook, React가 DOM을 변경하기 전에 발생
- 동적 CSS 삽입 가능
🪝Performance Hooks
불필요한 작업을 건너뛰어, 리렌더링 성능을 최적화시킨다.
useMemo
: 리렌더링하는 사이에 계산 결과를 캐시할 수 있게 해줌 ⇒ 메모이제이션
import { useMemo } from 'react';
function TodoList({ todos, tab }) {
const visibleTodos = useMemo(
() => filterTodos(todos, tab),
[todos, tab]
);
// ...
}
- calculation function : 캐시하려는 값을 계산하는 함수
- list of dependencies : 코드 내부에서 참조되는 반응 값의 리스트(props, state, 함수, ..)
useCallback
: 리렌더링하는 사이에 함수 정의를 캐시할 수 있게 해줌
import { useCallback } from 'react';
export default function ProductPage({ productId, referrer, theme }) {
const handleSubmit = useCallback((orderDetails) => {
post('/product/' + productId + '/buy', {
referrer,
orderDetails,
});
}, [productId, referrer]);
- fn : 다시 렌더링하는 사이에 캐시하려는 함수
- dependencies : 함수 내부에서 사용되는 요소 내의 모든 값을 포함한 값
useTransition
: UI를 차단하지 않고 상태를 업데이트할 수 있음
import { useTransition } from 'react';
function TabContainer() {
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('about');
function selectTab(nextTab) {
startTransition(() => {
setTab(nextTab);
});
}
// ...
}
- isPending : 우선순위가 낮은 일부 state 업데이트가 지연되고 있음을 알림
- startTransition : state 업데이트를 transition으로 표시할 수 있는 함수
- 낮은 우선순위로 처리해야 하는 경우, state 업데이트를 startTransition으로 래핑
- transition으로 표시된 setState는 다른 setState 업데이트 시 중단됨
useDeferredValue
: UI의 일부 업데이트를 연기할 수 있음
import { useState, useDeferredValue } from 'react';
function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
// ...
}
- value : 렌더링을 지연시키려는 값
📝 정리
생각보다 react hook의 종류가 이렇게까지 많을 줄은 몰랐다.. 특히 우선순위를 통해 성능을 최적화시키는 useTransition은 추후에 코드를 최적화할 때, 꼭 사용해야겠다는 생각이 들었다. 앞으로 프로젝트를 할 때 다양한 hook들을 사용하려는 시도를 많이 해봐야겠다!!!
🔗 참고
'React' 카테고리의 다른 글
[React] 구글 로그인 구현하기 (1) | 2024.12.04 |
---|---|
[React] lazy loading (2) | 2024.10.01 |
[React] Vite 사용하기 (1) | 2024.09.07 |
[React] Swiper.js 사용하여 슬라이더 구현하기 (0) | 2024.09.01 |
[React] 디자인 패턴 : MVC, MVVM, MVP, Flux (0) | 2024.08.22 |