데이터를 요청해야 할 때 마다 리듀서를 작성하는 것은 번거롭다.
매번 반복되는 코드를 커스텀 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;