[다시] React Hooks + TypeScript

2025. 3. 14. 10:46programming/React.js

 

1. useState와 TypeScript

상태(state)의 타입을 명시

✅ 기본 타입 지정 (string, number, boolean 등)

import { useState } from "react";

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

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
}

✅ 객체 상태 관리 (interface 사용)

interface User {
  name: string;
  age: number;
}

function UserProfile() {
  const [user, setUser] = useState<User | null>(null);

  return (
    <div>
      <button onClick={() => setUser({ name: "John", age: 30 })}>Set User</button>
      {user && <p>{user.name} - {user.age}</p>}
    </div>
  );
}

2. useEffect와 TypeScript

의존성 배열과 함께 사용하면 특정 값이 변경될 때만 실행

import { useState, useEffect } from "react";

function Timer() {
  const [count, setCount] = useState<number>(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCount((prev) => prev + 1);
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  return <p>Timer: {count}</p>;
}

3. useContext와 TypeScript

Context API를 사용할 때, createContext에 타입을 지정하여 사용.

import { createContext, useContext } from "react";

interface ThemeContextType {
  theme: "light" | "dark";
  toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextType | null>(null);

export function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) throw new Error("useTheme must be used within a ThemeProvider");
  return context;
}

function ThemedButton() {
  const { theme, toggleTheme } = useTheme();
  return (
    <button onClick={toggleTheme} style={{ background: theme === "dark" ? "#333" : "#fff" }}>
      Theme: {theme}
    </button>
  );
}

4. useReducer와 TypeScript

복잡한 상태 관리 useReducer 사용

import { useReducer } from "react";

interface State {
  count: number;
}

type Action = { type: "increment" } | { type: "decrement" };

function reducer(state: State, action: Action): State {
  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, { count: 0 });

  return (
    <div>
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
      <span>{state.count}</span>
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
    </div>
  );
}

5. useRef와 TypeScript

useRef는 DOM 요소를 조작하거나, 상태 유지에 사용

✅ DOM 요소 참조

import { useRef, useEffect } from "react";

function InputFocus() {
  const inputRef = useRef<HTMLInputElement | null>(null);

  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  return <input ref={inputRef} />;
}

✅ 값 저장 (current를 활용)

import { useRef } from "react";

function Stopwatch() {
  const countRef = useRef<number>(0);

  function increment() {
    countRef.current += 1;
    console.log("Count:", countRef.current);
  }

  return <button onClick={increment}>Increment</button>;
}

6. useCallback과 TypeScript

함수를 메모이제이션하여 불필요한 렌더링을 방지 ( 특정 함수가 불필요하게 다시 생성되는 것을 방지 ), 주로 자식 컴포넌트로 props로 전달되는 함수에 사용됩니다. 예를 들어, 다음과 같은 상황에서 유용. 특히, 자식 컴포넌트가 React.memo로 감싸져 있을 때 효과적

 

  • 의존성 배열 [] 안에 포함된 값들이 변경될 때만 함수가 다시 생성
  • 빈 배열 []이라면, 초기 렌더링 시에만 함수가 생성되고 이후에는 재사용된다.

 

import { useCallback, useState } from "react";

function Button({ onClick }: { onClick: () => void }) {
  return <button onClick={onClick}>Click</button>;
}

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

  const handleClick = useCallback(() => {
    setCount((prev) => prev + 1);
  }, []); //의존성 배열 [] 뎁스

  return <Button onClick={handleClick} />;
}

7. useMemo와 TypeScript

연산 비용이 큰 계산으로 인해 발생하는 성능 저하를 줄이는 데 도움을 줍니다.

특정 값(value)을 메모이제이션합니다. -> 연산 결과를 저장하여 불필요한 재계산을 방지하고 싶을 때 사용합니다.

import { useMemo, useState } from "react";

function ExpensiveComponent({ num }: { num: number }) {
  const expensiveValue = useMemo(() => {
    console.log("Calculating...");
    return num ** 2;
  }, [num]);

  return <p>Result: {expensiveValue}</p>;
}

8. 커스텀 Hook 만들기 (Custom Hook + TypeScript)

import { useState, useEffect } from "react";

function useFetch(url: string): T | null {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch(url)
      .then((res) => res.json())
      .then((data) => setData(data));
  }, [url]);

  return data;
}

function App() {
  const data = useFetch<{ title: string }>("https://jsonplaceholder.typicode.com/todos/1");

  return {JSON.stringify(data, null, 2)}; 
  }

🚀 정리

  • useState<T>() → 상태의 타입 지정
  • useEffect → 부수 효과 처리
  • useContext<T>() → 전역 상태 관리
  • useReducer<State, Action>() → 복잡한 상태 관리
  • useRef<T>() → DOM 요소 및 값 저장
  • useCallback(()=>{},[]), useMemo → 성능 최적화
  • 커스텀 Hook코드 재사용성 향상