React

useAsync 커스텀 Hook 만들기

하루 2022. 6. 7. 17:45

데이터를 요청해야 할 때 마다 리듀서를 작성하는 것은 번거롭다.

매번 반복되는 코드를 커스텀 Hook을 만들어서 요청 상태 관리 로직을 쉽게 재사용 하는 방법을 알아보자.

 

useAsync.js

import { useReducer, useEffect, useCallback } from "react";

function reducer(state, action) {
    switch (action.type) {
        case "LOADING":
            return {
                loading: true,
                data: null,
                error: null,
            };
        case "SUCCESS":
            return {
                loading: false,
                data: action.data,
                error: null,
            };
        case "ERROR":
            return {
                loading: false,
                data: null,
                error: action.error,
            };
        default:
            throw new Error(`Unhandled action type: ${action.type}`);
    }
}

function useAsync(callback, deps = []) {
    // callback 에는 API를 호출하는 함수
    const [state, dispatch] = useReducer(reducer, {
        loading: false,
        data: null,
        error: null,
    });

    const fetchData = useCallback(async () => {
        dispatch({ type: "LOADING" });
        try {
            const data = await callback();
            dispatch({ type: "SUCCESS", data });
        } catch (e) {
            dispatch({ type: "ERROR", error: e });
        }
    }, [callback]);

    useEffect(() => {
        fetchData();
        // eslint-disable-next-line
    }, deps);

    return [state, fetchData];
}

export default useAsync;

Users.js

import React from "react";
import axios from "axios";
import useAsync from "./useAsync";

// useAsync.js Hook 함수로 넘겨주는 callback 함수
async function getUsers() {
    const response = await axios.get(
        "http://jsonplaceholder.typicode.com/users/"
    );
    return response.data;
}

function Users() {
    const [state, refetch] = useAsync(getUsers);

    // users, loading, error 3가지 상태에 따라 다른 결과물을 반환하도록 설정
    const { loading, error, data: users } = state;
    if (loading) return <div>로딩중..</div>;
    if (error) return <div>에러가 발생했습니다</div>;
    if (!users) return null;

    return (
        <>
            <ul>
                {users.map((user) => (
                    <li key={user.id}>
                        {user.username} ({user.name})
                    </li>
                ))}
            </ul>
            <button onClick={refetch}>다시 불러오기</button>
        </>
    );
}

export default Users;