React, Hook 정리

2024. 12. 16. 11:24카테고리 없음

요약

기본 상태 및 생애주기 훅 useState 함수형 컴포넌트에서 로컬 상태를 관리합니다.
useEffect 함수형 컴포넌트에서 부수 효과를 처리합니다 (예: 데이터 가져오기, 구독).
useContext React 컨텍스트에서 값을 소비합니다.
참조 및 DOM 훅 useRef 렌더링 간에 지속되는 가변 참조 객체를 생성합니다.
useLayoutEffect useEffect와 비슷하지만, DOM 변경 후 동기적으로 실행됩니다.
성능 및 최적화 훅 useMemo 값을 메모이제이션하여 렌더링할 때 비싼 계산을 피합니다.
useCallback 함수가 불필요하게 새로 생성되는 것을 방지합니다.
useDeferredValue 값 업데이트를 지연시켜 UI 차단을 방지합니다.
useTransition 업데이트를 "전환"으로 표시하여 UI를 차단하지 않도록 지연시킬 수 있습니다.
고급 상태 관리 훅 useReducer 복잡한 상태 로직이나 이전 상태에 의존하는 상태 관리에 유용합니다.
useImperativeHandle 컴포넌트에서 ref를 사용할 때 노출되는 인스턴스 값을 커스터마이징합니다.
기타 useSyncExternalStore 외부 상태 구독
  useId 고유한 ID 생성 (주로 접근성 관련)

 

                      +--------------------------------------+
                      |              React 훅               |
                      +--------------------------------------+
                                  /         |        \
                                 /          |         \
                 +------------+------------+------------+-----------------+
                 |            |            |            |                 |
           +-----+-----+  +---+--------+ +---------+   +--------------+    +---------------+
           | useState  |  | useEffect  | | useContext|   | useRef      |    | useReducer   |
           +-----------+  +------------+ +-----------+   +-------------+    +---------------+
                            /                  \
                      +-----+-----+          +-----+------+
                      | useMemo   |          | useCallback |
                      +-----------+          +-------------+
                      /                     \
            +--------+--------+       +------++--------+
            | useLayoutEffect  |       | useTransition |
            +------------------+       +---------------+
                    |
            +-------+--------+
            | useDeferredValue|
            +-----------------+

 

 

1. 기본 상태 및 생애주기 훅

1.1 useState (상태 관리)

목적 함수형 컴포넌트에서 상태를 관리
사용 시점 함수형 컴포넌트에서 상태를 관리할 때
반환 값 상태와 상태를 갱신하는 함수가 담긴 배열
주요 특성 상태 변경 시 컴포넌트가 리렌더링됨.
import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>현재 카운트: {count}</p>
      <button onClick={() => setCount(count + 1)}>증가</button>
    </div>
  );
}

export default Counter;

 

useState는 상태를 관리하는 데 사용됩니다. 버튼을 클릭할 때마다 상태가 변경됩니다.

 

 

1.2 useEffect  (부수 효과)

목적 부수 효과(예: 데이터 fetching, DOM 업데이트, 구독)를 처리
사용 시점 컴포넌트 렌더링 후 부수 효과를 처리할 때
반환 값 undefined 또는 클린업 함수(선택적)
주요 특성 렌더링 후 실행되며, 클린업 함수 지원.
useEffect(() => {
  // 랜더링 될 때마다
  console.log('effect');
  return () => {
    // 컴포넌트가 언마운트 될 때
    console.log('cleanup');
  }
}, [name]); // name 값이 업데이트 될 때만 실행하려면 값 입력
import React, { useState, useEffect } from 'react';

function Timer() {
  const [time, setTime] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setTime(prevTime => prevTime + 1);
    }, 1000);

    // Cleanup 함수
    return () => clearInterval(interval);
  }, []); // 컴포넌트가 마운트될 때만 실행

  return <div>시간: {time}초</div>;
}

export default Timer;

 

useEffect는 컴포넌트가 렌더링된 후 부수 효과를 처리합니다. 위 예제에서는 타이머를 설정합니다.

 

 

1.3 useContext (Context 사용)

목적 React Context 값을 소비
사용 시점 Context에서 제공하는 값을 소비할 때
반환 값 현재 Context 값
주요 특성 컴포넌트 트리 전체에서 값을 공유.
import React, { useState, useContext, createContext } from 'react';

// Context 생성
const ThemeContext = createContext();

function ThemedComponent() {
  const theme = useContext(ThemeContext);
  return <div style={{ background: theme.background, color: theme.color }}>테마: {theme.name}</div>;
}

function App() {
  const theme = {
    name: '다크 모드',
    background: '#333',
    color: '#fff',
  };

  return (
    <ThemeContext.Provider value={theme}>
      <ThemedComponent />
    </ThemeContext.Provider>
  );
}

export default App;

 

useContext는 React 컨텍스트에서 값을 가져오는 데 사용됩니다. ThemeContext에서 테마를 소비하고 이를 컴포넌트에 반영합니다.

 

2.참조 및 DOM 훅

2.1 useRef (참조 관리)

목적 DOM 요소나 변수를 위한 참조를 관리
사용 시점 DOM 요소에 접근하거나 렌더 간 값을 저장할 때
반환 값 current 속성을 가진 ref 객체
주요 특성 리렌더링을 트리거하지 않음.
import React, { useRef } from 'react';

function FocusInput() {
  const inputRef = useRef();

  const handleFocus = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={handleFocus}>입력란에 포커스</button>
    </div>
  );
}

export default FocusInput;

 

useRef는 DOM 요소에 대한 참조를 관리합니다. 위 예제에서는 버튼을 클릭하면 입력란에 포커스를 설정합니다.

 

 

2.2 useLayoutEffect (레이아웃 후 효과)

목적 DOM 업데이트 후 동기적으로 부수 효과를 실행
사용 시점 DOM을 읽거나 수정해야 할 때
반환 값 undefined 또는 클린업 함수(선택적)
주요 특성 DOM 업데이트 후 화면에 그려지기 전에 실행됨.
import React, { useLayoutEffect, useState } from 'react';

function LayoutEffectExample() {
  const [width, setWidth] = useState(0);

  useLayoutEffect(() => {
    const handleResize = () => {
      setWidth(window.innerWidth);
    };

    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []); // 처음 렌더링될 때만 실행

  return <div>화면 너비: {width}px</div>;
}

export default LayoutEffectExample;

 

useLayoutEffect는 레이아웃이 변경된 후 DOM을 동기적으로 업데이트할 때 사용합니다.

 

3.성능 및 최적화 훅

3.1 useMemo (값 메모이제이션)

목적 값의 계산을 메모이제이션하여 불필요한 계산을 방지
사용 시점 비싼 계산을 최적화하려 할 때
반환 값 메모이제이션된 값
주요 특성 의존성 배열이 변경될 때만 재계산됨.
import React, { useMemo, useState } from 'react';

function ExpensiveComputation({ num }) {
  const computeExpensiveValue = (num) => {
    console.log('계산 중...');
    return num * 2;
  };

  const memoizedValue = useMemo(() => computeExpensiveValue(num), [num]);

  return <div>계산된 값: {memoizedValue}</div>;
}

function App() {
  const [num, setNum] = useState(0);

  return (
    <div>
      <ExpensiveComputation num={num} />
      <button onClick={() => setNum(num + 1)}>증가</button>
    </div>
  );
}

export default App;

 

useMemo는 계산이 많은 값이 변경되었을 때만 재계산되도록 최적화합니다.

 

 

3.2 useCallback (함수 메모이제이션)

목적 함수를 메모이제이션하여 불필요한 재생성을 방지
사용 시점 함수를 props로 전달하고 불필요한 리렌더링을 방지할 때
반환 값 메모이제이션된 함수
주요 특성 함수의 재생성을 방지함.
import React, { useCallback, useState } from 'react';

function Button({ onClick }) {
  return <button onClick={onClick}>클릭</button>;
}

function App() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]); // count가 변경될 때만 함수 재생성

  return (
    <div>
      <Button onClick={handleClick} />
      <p>클릭 횟수: {count}</p>
    </div>
  );
}

export default App;

 

useCallback은 함수가 불필요하게 재생성되는 것을 방지하여 성능을 최적화합니다.

 

 

3.3 useDeferredValue (UI 또는 필터링 지연되는 값 처리)

목적 덜 중요한 UI 업데이트를 지연시켜 렌더링 최적화
사용 시점 중요한 업데이트를 먼저 처리하고 덜 중요한 업데이트는 지연시킬 때
반환 값 지연된 값
주요 특성 UI 업데이트의 우선순위를 조절. (React 18 기능)
import React, { useState, useDeferredValue } from 'react';

function Search() {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);  // 지연된 값

  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="검색어 입력"
      />
      <p>입력된 값: {deferredQuery}</p> {/* 지연된 입력값 */}
    </div>
  );
}

export default Search;

 

useDeferredValue는 UI 업데이트를 지연시켜 우선순위가 높은 업데이트가 먼저 처리되도록 합니다. 주로 입력 필드와 같은 비동기적인 상황에서 사용됩니다.

 

 

3.4 useTransition (UI 업데이트 비동기 처리)

목적 비동기적 렌더링을 처리하여 UI 반응성을 유지
사용 시점 긴 렌더링 작업을 비동기적으로 처리하고 UI를 부드럽게 유지할 때
반환 값 isPending (boolean)과 startTransition (function)
주요 특성 긴 렌더링 작업을 비동기적으로 처리하여 UI 반응성을 유지.
import React, { useState, useTransition } from 'react';

function Filter() {
  const [filter, setFilter] = useState('');
  const [isPending, startTransition] = useTransition(); // 트랜지션 시작

  const data = Array.from({ length: 1000 }, (_, i) => `Item ${i + 1}`);
  const filteredData = data.filter(item => item.includes(filter));

  const handleFilterChange = (e) => {
    startTransition(() => {
      setFilter(e.target.value);  // 비동기적으로 상태 업데이트
    });
  };

  return (
    <div>
      <input
        type="text"
        value={filter}
        onChange={handleFilterChange}
        placeholder="필터 입력"
      />
      {isPending ? <p>로딩 중...</p> : null}
      <div>
        {filteredData.map(item => <div key={item}>{item}</div>)}
      </div>
    </div>
  );
}

export default Filter;

 

useTransition은 UI 업데이트를 비동기적으로 처리하여 렌더링 차단을 최소화합니다. 주로 긴 작업이나 대량 데이터 처리에서 UI의 반응성을 유지하기 위해 사용됩니다.

 

4. 고급 상태 관리 훅

4.1 useReducer (복잡한 상태 관리)

목적 복잡한 상태 로직을 관리하는 데 사용
사용 시점 상태 로직이 복잡할 때 (예: 여러 상태 변수, 액션 등)
반환 값 현재 상태와 dispatch 함수
주요 특성 복잡한 상태 전환을 처리하는 데 유용.
import React, { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>현재 카운트: {state.count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>증가</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>감소</button>
    </div>
  );
}

export default Counter;

 

 

4.2 useImperativeHandle (Ref 커스터마이징)

목적 부모 컴포넌트가 ref로 접근할 수 있는 값을 커스터마이징
사용 시점 부모 컴포넌트에 특정 메서드나 값을 노출할 때
반환 값 부모에게 노출할 메서드나 값을 담은 객체
주요 특성 forwardRef와 함께 사용하여 부모가 접근할 값을 제어
import React, { useState, useRef, useImperativeHandle, forwardRef } from 'react';

const ChildComponent = forwardRef((props, ref) => {
  const [count, setCount] = useState(0);

  // 부모 컴포넌트에 노출할 메서드 정의
  useImperativeHandle(ref, () => ({
    increment: () => setCount(count + 1),
    decrement: () => setCount(count - 1),
  }));

  return <div>자식 컴포넌트 카운트: {count}</div>;
});

function ParentComponent() {
  const childRef = useRef();

  return (
    <div>
      <ChildComponent ref={childRef} />
      <button onClick={() => childRef.current.increment()}>자식 증가</button>
      <button onClick={() => childRef.current.decrement()}>자식 감소</button>
    </div>
  );
}

export default ParentComponent;

 

useImperativeHandle은 부모 컴포넌트가 자식 컴포넌트의 ref를 통해 호출할 수 있는 메서드를 정의합니다.

 

5.기타 (정리 필요)

useSyncExternalStore

목적 외부 상태를 구독하여 상태 업데이트를 처리
사용 시점 외부 스토어나 상태 관리 시스템에 구독할 때
반환 값 외부 스토어의 현재 상태
주요 특성 외부 상태 구독 시 사용.
import React, { useSyncExternalStore } from 'react';

// 예시 외부 스토어 (간단한 구현)
let currentState = 0;
const listeners = new Set();

function subscribe(callback) {
  listeners.add(callback);
  return () => listeners.delete(callback);
}

function getState() {
  return currentState;
}

function setState(value) {
  currentState = value;
  listeners.forEach(callback => callback());
}

function Counter() {
  // useSyncExternalStore를 사용해 외부 스토어의 상태 구독
  const state = useSyncExternalStore(subscribe, getState);

  return (
    <div>
      <p>현재 카운트: {state}</p>
      <button onClick={() => setState(state + 1)}>증가</button>
    </div>
  );
}

export default Counter;

 

외부 상태를 구독하고, React의 상태와 동기화합니다. React 컴포넌트에서 외부 상태(예: Redux, Zustand 등)나 외부 데이터 소스를 안전하게 구독할 수 있게 합니다.

 

useId

목적 컴포넌트 렌더링 간 유니크한 ID를 생성
사용 시점 접근성, 폼 입력 등에서 고유한 ID가 필요할 때
반환 값 유니크한 문자열 ID
주요 특성 렌더링 간 고유한 ID 생성 (주로 접근성 관련).
import React, { useId } from 'react';

function Form() {
  const id = useId();  // Generate a unique ID

  return (
    <div>
      <label htmlFor={`${id}-input`}>이메일:</label>
      <input id={`${id}-input`} type="email" placeholder="이메일 입력" />
    </div>
  );
}

export default Form;

 

이렇게 하면 컴포넌트가 여러 번 렌더링되더라도 각 렌더링에서 고유한 ID가 유지되어 다른 요소와 충돌하지 않게 됩니다. useId는 주로 폼 요소나 접근성에 중요한 요소에서 유용하게 사용됩니다.

 

연습

https://playcode.io/2196579