커스텀 훅은 특정한 로직을 캡슐화하여 코드의 가독성과 유지보수성을 향상시키고, 여러 컴포넌트에서 동일한 로직을 재사용할 수 있게 해준다.
userId
를 인자로 받아와 서버로부터 해당 사용자의 데이터를 받아오는 커스텀 훅
import { useState, useEffect } from 'react';
import axios from 'axios';
export const useUser = (userId) => {
const [user, setUser] = useState(null);
useEffect(() => {
(async () => {
const response = await axios.get(`http://localhost:3000/users/${userId}`);
setUser(response.data);
})();
}, [userId]);
return user;
};
import { useUser } from './useUser';
export const UserInfo = ({ userId }) => {
const user = useUser(userId);
const { name, age, hairColor, hobbies } = user || {};
return (
// 생략...
);
};
url을 인자로 받아와 서버로부터 리소스를 받아오는 커스텀 훅
import { useState, useEffect } from 'react';
import axios from 'axios';
export const useResource = (resourceUrl) => {
const [resource, setResource] = useState(null);
useEffect(() => {
(async () => {
const response = await axios.get(resourceUrl);
setResource(response.data);
})();
}, [resourceUrl]);
return resource;
};
useResource
를 재사용하여 useUser
를 대체하도록 할 수 있다.
import { useResource } from './useResource';
export const UserInfo = ({ userId }) => {
const user = useResource(`http://localhost:3000/users/${userId}`);
const { name, age, hairColor, hobbies } = user || {};
return (
// 생략...
);
};
UserInfo 컴포넌트 외에도 다른 컴포넌트에서도 사용할 수 있어 재사용성이 더 높다.
import { useResource } from './useResource';
export const ProductInfo = ({ productId }) => {
const product = useResource(`http://localhost:3000/products/${productId}`);
const { name, price, description, rating } = product || {};
return (
// 생략...
);
};
매개변수로 함수 자체를 받아 결과를 리턴하도록 작성할 수도 있다.
import { useState, useEffect, useCallback } from 'react';
export const useDataSource = (getResourceFunc) => {
const [resource, setResource] = useState(null);
useCallback(() => { // useCallback으로 변경
(async () => {
const result = await getResourceFunc();
setResource(result);
})();
}, [getResourceFunc]);
return resource;
};
❗️이때 useEffect를 사용하면 서버로 요청이 무한하게 보내지는 현상이 발생한다.
useEffect는 의존성 배열에 있는
getResourceFunc
함수의 변경이 감지되면 실행되도록 되어있다.
그런데getResourceFunc
함수는useDataSource
훅이 호출될 때마다 새롭게 생성되기 때문에(다른 참조값을 가짐)getResourceFunc
함수는 변경되며, 이로 인해 useEffect 훅이 계속해서 실행되고 서버 요청이 무한히 보내지는 문제가 발생한다.
이를 해결하기 위해서는 useCallback 훅을 사용하여 getResourceFunc
함수를 캐싱하고 의존성 배열로 전달해야 한다.
useCallback 훅은 매번 새로운 함수를 생성하지 않고, 이전에 생성한 함수를 재사용하기 때문에 서버 요청을 1번만 보낸다.
import { useDataSource } from './useDataSource';
import axios from 'axios';
const serverResource = (resourceUrl) => async () => {
const response = await axios.get(resourceUrl);
return response.data;
};
const localStorageResource = (key) => () => {
return localStorage.getItem(key);
};
export const UserInfo = ({ userId }) => {
const user = useDataSource(serverResource(`http://localhost:3000/users/${userId}`));
const message = useDataSource(localStorageResource('message'));
const { name, age, hairColor, hobbies } = user || {};
return (
// 생략...
);
};
이 글은 아래 링크를 참고하여 작성한 글입니다.
Custom Hooks | React Design Pattern - 5
훅훅 들어오네요! 고생하셨습니다.