[React 디자인 패턴] Custom Hooks

지은·2023년 6월 15일
4

⚛️ React

목록 보기
22/23
post-custom-banner

Custom Hook

커스텀 훅은 특정한 로직을 캡슐화하여 코드의 가독성과 유지보수성을 향상시키고, 여러 컴포넌트에서 동일한 로직을 재사용할 수 있게 해준다.

🪝 useUser Hook

userId를 인자로 받아와 서버로부터 해당 사용자의 데이터를 받아오는 커스텀 훅

useUser.js

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;
};

UserInfo.js

import { useUser } from './useUser';

export const UserInfo = ({ userId }) => {
	const user = useUser(userId);
	const { name, age, hairColor, hobbies } = user || {};

	return (
    	// 생략...
    );
};

🪝 useResource Hook

useResource.js

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;
};

UserInfo.js

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 (
    	// 생략...
    );
};

ProductInfo.js

UserInfo 컴포넌트 외에도 다른 컴포넌트에서도 사용할 수 있어 재사용성이 더 높다.

import { useResource } from './useResource';

export const ProductInfo = ({ productId }) => {
	const product = useResource(`http://localhost:3000/products/${productId}`);
	const { name, price, description, rating } = product || {};

	return (
    	// 생략...
    );
};

🪝 useDataSource Hook

매개변수로 함수 자체를 받아 결과를 리턴하도록 작성할 수도 있다.

useDataSource.js

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번만 보낸다.

UserInfo.js

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

profile
블로그 이전 -> https://janechun.tistory.com
post-custom-banner

4개의 댓글

comment-user-thumbnail
2023년 6월 16일

훅훅 들어오네요! 고생하셨습니다.

답글 달기
comment-user-thumbnail
2023년 6월 18일

오 이용하기 좋은 훅들이네요! 잘 보고 갑니다~

답글 달기
comment-user-thumbnail
2023년 6월 18일

고생하셨습니다 ~~!

답글 달기
comment-user-thumbnail
2023년 6월 18일

헉 덕분에 코드 수정하러 가봐야겠습니다 ! 고생하셨습니당 ㅎㅎ

답글 달기