커스텀 훅은 리액트에서 제공하는 일반적인 훅(useState
, userEffect
, useContext
)과 동일한 규칙과 규약을 따른다.
커스텀 훅을 사용하면 복잡한 로직을 커스텀 훅으로 추상화하고 이 로직을 컴포넌트 코드 밖에서 분리함으로써, 컴포넌트 코드가 더 간결해지고 가독성이 향상된다.
커스텀 훅을 사용하여 공통 기능을 여러 컴포넌트 간에 공유할 수 있다. 이는 코드 중복을 방지하고 코드 재사용성을 높이는데 도움이 된다.
예를 들어, 데이터 가져오기, 양식 처리, 인증, 로깅과 같은 일반적인 작업을 하는 커스텀 훅을 만들어 다른 컴포넌트에서 쉽게 재사용할 수 있다.
import { useState } from 'react';
const ToggleExample = () => {
const [show, setShow] = useState(false);
return (
<div>
<h4>toggle custom hook</h4>
<button className='btn' onClick={() => setShow(!show)}>
toggle
</button>
{show && <h4>some stuff</h4>}
</div>
);
};
export default ToggleExample;
토글로 렌더링 여부를 상태 값으로 처리하는 기능이 컴포넌트에 속해있다
이러한 기능을 다른 컴포넌트에서 공통적으로 사용할 경우 toggle에 대한 커스텀 훅을 만들어 사용하면 하나의 코드로 여러곳에서 사용할 수 있으며, 컴포넌트 외부에 기능을 만들어 컴포넌트의 코드가 간결해진다.
useToggle.js
import { useState } from "react";
const useToggle = (defaultValue) => {
const [show, setShow] = useState(defaultValue);
const toggle = () => {
setShow(!show)
}
return {show, toggle};
}
export default useToggle;
import useToggle from './useToggle';
const ToggleExample = () => {
const {show, toggle} = useToggle(true);
return (
<div>
<h4>toggle custom hook</h4>
<button className='btn' onClick={toggle}>
toggle
</button>
{show && <h4>some stuff</h4>}
</div>
);
};
export default ToggleExample;
fetch-data.js
import { useEffect, useState } from 'react';
const url = 'https://api.github.com/users/Jaeseokkong';
const FetchData = () => {
const [isLoading, setIsLoading] = useState(true);
const [isError, setIsError] = useState(false);
const [user, setUser] = useState(null);
useEffect(() => {
const fetchUser = async () => {
try {
const resp = await fetch(url);
// console.log(resp);
if (!resp.ok) {
setIsError(true);
setIsLoading(false);
return;
}
const user = await resp.json();
setUser(user);
} catch (error) {
setIsError(true);
// console.log(error);
}
// hide loading
setIsLoading(false);
};
fetchUser();
}, []);
// order matters
// don't place user JSX before loading or error
if (isLoading) {
return <h2>Loading...</h2>;
}
if (isError) {
return <h2>There was an error...</h2>;
}
const { avatar_url, name, company, bio } = user;
return (
<div>
<img
style={{ width: '100px', borderRadius: '25px' }}
src={avatar_url}
alt={name}
/>
<h2>{name}</h2>
<h4>works at {company}</h4>
<p>{bio}</p>
</div>
);
};
export default FetchData;
useFetchPerson.js
import { useEffect, useState } from "react";
const useFetch = (url) => {
const [isLoading, setIsLoading] = useState(true);
const [isError, setIsError] = useState(false);
const [user, setUser] = useState(null);
useEffect(() => {
const fetchUser = async () => {
try {
const resp = await fetch(url);
// console.log(resp);
if (!resp.ok) {
setIsError(true);
setIsLoading(false);
return;
}
const user = await resp.json();
setUser(user);
} catch (error) {
setIsError(true);
// console.log(error);
}
// hide loading
setIsLoading(false);
};
fetchUser();
},[]);
return {isLoading, isError, user}
}
export default useFetch;
import { useEffect, useState } from 'react';
import useFetch from '../starter/useFetch';
const url = 'https://api.github.com/users/Jaeseokkong';
const FetchData = () => {
const {isLoading, isError, user} = useFetch(url);
// order matters
// don't place user JSX before loading or error
if (isLoading) {
return <h2>Loading...</h2>;
}
if (isError) {
return <h2>There was an error...</h2>;
}
const { avatar_url, name, company, bio } = user;
return (
<div>
<img
style={{ width: '1![](https://velog.velcdn.com/images/wotjr294/post/b7f7b9a1-6f86-4c20-a4d1-49a53e0ab610/image.gif)
00px', borderRadius: '25px' }}
src={avatar_url}
alt={name}
/>
<h2>{name}</h2>
<h4>works at {company}</h4>
<p>{bio}</p>
</div>
);
};
export default FetchData;
공통적으로 만들어서도 사용가능
useFecth.js
import { useEffect, useState } from "react";
const useFetch = (url) => {
const [isLoading, setIsLoading] = useState(true);
const [isError, setIsError] = useState(false);
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const resp = await fetch(url);
// console.log(resp);
if (!resp.ok) {
setIsError(true);
setIsLoading(false);
return;
}
const response = await resp.json();
setData(response);
} catch (error) {
setIsError(true);
// console.log(error);
}
// hide loading
setIsLoading(false);
};
fetchData();
},[]);
return {isLoading, isError, data}
}
export default useFetch;
import { useEffect, useState } from 'react';
import useFetch from './useFetch';
const url = 'https://api.github.com/users/Jaeseokkong';
const FetchData = () => {
const {isLoading, isError, data: user} = useFetch(url);
// order matters
// don't place user JSX before loading or error
if (isLoading) {
return <h2>Loading...</h2>;
}
if (isError) {
return <h2>There was an error...</h2>;
}
const { avatar_url, name, company, bio } = user;
return (
<div>
<img
style={{ width: '100px', borderRadius: '25px' }}
src={avatar_url}
alt={name}
/>
<h2>{name}</h2>
<h4>works at {company}</h4>
<p>{bio}</p>
</div>
);
};
export default FetchData;
response 값을 데이터로 넣고 커스텀훅을 사용하는 컴포넌트에서 data의 값을 지정하여 사용한다.