React Hooks

cloud2000·2025년 11월 13일

React hook에 대해 알아본다.

useState() 사용예

"use client";

import { useState } from "react";

const CountUseStatePage = () => {
	console.log("CountUsageStatePage render");

	const [count, SetCount] = useState(0);
	const clickHandler = () => {
		// React의 일괄업데이트(batch update)로 setCount(count+1)을 하면 0+1, 0+1, 0+1을 반복하게 됨.
		// 콜백함수를 사용함으로써 이전 상태값을 매개변수로 새로운 상태를 안전하게 계산함.
		SetCount((count) => count + 1);
		SetCount((count) => count + 1);
		SetCount((count) => count + 1);
	};

	// 초기값에 null을 넣더라도 나중에 저장할 타입까지 고려해 제너릭을 명시함.
	const [name, setName] = useState<string | null>(null);
	const [age, setAge] = useState<number | null>(null);
	const [gender, setGender] = useState<string | null>(null);

	// 개발 상태값을 객체로 묶어서 관리할 수 있음.
	const [formState, setFormState] = useState({
		name: "",
		age: 0,
		gender: "",
	});

	const clickHandler2 = () => {
		setName("Cloud");
		setAge(30);
		setGender("female");
	};

	const clickHandler3 = () => {
		setFormState({ ...formState, age: 20, name: "정대운", gender: "male" });
	};

	return (
		<>
			<div style={{ display: "flex" }}>
				<h3 style={{ margin: "10px" }}>Count: {count}</h3>
				<button onClick={clickHandler}>증가</button>
			</div>
			<div>
				<p>이름: {name}</p>
				<p>나이: {age}</p>
				<p>성별: {gender}</p>
				<button onClick={clickHandler2}>변경</button>
			</div>
			<div>
				<p>이름: {formState.name}</p>
				<p>나이: {formState.age}</p>
				<p>성별: {formState.gender}</p>
				<button onClick={clickHandler3}>변경</button>
			</div>
		</>
	);
};

export default CountUseStatePage;

useReducer() 사용 예

  • reducer/counterReducer.ts
export function counterReducer(state: number, action: { type: string }) {
	switch (action.type) {
		case "INCREMENT":
			return state + 1;
		case "DECREMENT":
			return state - 1;
		case "RESET":
			return 0;
		default:
			throw new Error(`Unhandled action type: ${action.type}`);
	}
}
  • reducer 사용하는 page
"use client";

import { useReducer } from "react";
import { counterReducer } from "../reducer/counterReducer";

const CountReducerPage = () => {
	console.log("CountReducerPage render");
	const [count, dispatch] = useReducer(counterReducer, 0);

	return (
		<>
			<h1>Count: {count}</h1>
			<button onClick={() => dispatch({ type: "DECREMENT" })}>감소</button>
			<button onClick={() => dispatch({ type: "RESET" })}>초기화</button>
			<button onClick={() => dispatch({ type: "INCREMENT" })}>증가</button>
		</>
	);
};

export default CountReducerPage;

React.Memo, useCallback() 사용예

  • useState 훅으로 정의한 상태를 변경할 때 이전 상태 값을 기준으로 새 값을 계산해야 하는 경우에는 상태 변경 함수를 콜백함수 형태로 사용하는 것이 안전하다.
"use client";
import { memo, useCallback, useState } from "react";

const MemoA = memo(function A() {
	console.log("MemoA is rendered");
	return (
		<>
			<div>Child Component</div>
		</>
	);
});

const CountPage = () => {
	console.log("CountPage render");
	const [count, setCount] = useState(0);
    
    // const increment = useCallback(() => setCount(count + 1), []);
	const increment = useCallback(() => setCount((count) => count + 1), []);
	return (
		<>
			<h1>App Count: {count}</h1>
			<button onClick={increment}>증가</button>
			<MemoA />
		</>
	);
};

export default CountPage;

useFetch 커스텀 훅 구현예

import { useState, useEffect } from "react";

const useFetch = <T>(url: string, initialData: T) => {
	const [data, setData] = useState<T>(initialData);
	const [error, setError] = useState<string>("");
	const [isLoading, setIsLoading] = useState<boolean>(true);

	useEffect(() => {
		const controller = new AbortController();
		const signal = controller.signal;

		const fetchData = async () => {
			try {
				const response = await fetch(url, { signal });
				if (!response.ok) {
					throw new Error("데이터를 불러오지 못했습니다.");
				}
				const data = await response.json();
				setData(data);
				setIsLoading(false);
			} catch (error) {
				setError(
					error instanceof Error
						? error.message
						: "알 수 없는 오류가 발생했습니다."
				);
				setIsLoading(false);
			} finally {
				setIsLoading(false);
			}
		};

		fetchData();
		return () => {
			controller.abort();
		};
	}, [url]);

	return { data, error, isLoading };
};

export default useFetch;
  • 사용하는 컴포넌트에서...
import useFetch from "./useFetch";

const App = () => {
	const { data, error, isLoading } = useFetch<
		{ id: number; title: string; completed: boolean }[]
	>("https://jsonplaceholder.typicode.com/todos", []);

	if (isLoading) return <div>로딩 중 ...</div>;
	if (error) return <div>{error}</div>;

	return (
		<div>
			{data.map((todo) => (
				<li key={todo.id}>{todo.title}</li>
			))}
		</div>
	);
};
profile
클라우드쟁이

0개의 댓글