- 개요
리액트에는 useState, useContext, useEffect 같은 빌트인 훅들이 있다.
⇒ 커스텀 훅은 리액트에서 더 구체적인 목적을 위한 훅을 만들기 위함으로 필요에 따라서 자신만의 훅을 만들 수 있다.
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
function handleOnline() {
setIsOnline(true);
}
function handleOffline() {
setIsOnline(false);
}
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline; //리턴값 : 컴포넌트들이 이 값을 읽을 수 있다.
}
import { useOnlineStatus } from './useOnlineStatus.js';
//컴포넌트1
function StatusBar() {
const isOnline = useOnlineStatus();
return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;
}
//컴포넌트2
function SaveButton() {
const isOnline = useOnlineStatus();
function handleSaveClick() {
console.log('✅ Progress saved');
}
return (
<button disabled={!isOnline} onClick={handleSaveClick}>
{isOnline ? 'Save progress' : 'Reconnecting...'}
</button>
);
}
export default function App() {
return (
<>
<SaveButton />
<StatusBar />
</>
);
}
⇒ 커스텀 훅을 사용함으로써, 컴포넌트 내부의 코드가 어떻게 할것인가가 아닌, “무엇을 할것인가”를 설명한다.
⇒ 지저분한 세부 사항을 숨길 수 있다. (컴포넌트의 내부 코드는 구현이 아니라 의도를 표현)
커스텀 훅은 중복되는 로직을 재사용하고 싶을 때 사용할 수 있지만 그렇다고 중복되는 모든 코드에 대해 커스텀 훅을 추출하면 불필요하게 사용될 수 있어 적절한 상황에 사용해야 한다.
Effect를 작성할때마다 커스텀 훅으로 감싸는 것이 좋을지 생각해본다. Effect를 커스텀 훅으로 감싸면 의도와 데이터 흐름 방식을 정확하게 전달 가능
예시> 도시 목록을 표시하는 드롭다운과 선택한 도시의 지역 목록을 표시하는 드롭다운 두 개를 표시
function ShippingForm({ country }) {
const [cities, setCities] = useState(null);
// This Effect fetches cities for a country
// 이 Effect는 국가의 도시들을 페치합니다
useEffect(() => {
let ignore = false;
fetch(`/api/cities?country=${country}`)
.then(response => response.json())
.then(json => {
if (!ignore) {
setCities(json);
}
});
return () => {
ignore = true;
};
}, [country]);
const [city, setCity] = useState(null);
const [areas, setAreas] = useState(null);
// This Effect fetches areas for the selected city
// 이 Effect는 선택된 도시의 장소들을 페치합니다
useEffect(() => {
if (city) {
let ignore = false;
fetch(`/api/areas?city=${city}`)
.then(response => response.json())
.then(json => {
if (!ignore) {
setAreas(json);
}
});
return () => {
ignore = true;
};
}
}, [city]);
// ...
url
을 입력하면 data
를 가져올 수 있다.
//커스텀 훅 useData
function useData(url) {
const [data, setData] = useState(null);
useEffect(() => {
if (url) {
let ignore = false;
fetch(url)
.then(response => response.json())
.then(json => {
if (!ignore) {
setData(json);
}
});
return () => {
ignore = true;
};
}
}, [url]);
return data;
}
function ShippingForm({ country }) {
const cities = useData(`/api/cities?country=${country}`);
const [city, setCity] = useState(null);
const areas = useData(city ? `/api/areas?city=${city}` : null);
// ...
useData
안에 Effect를 “숨기면” ShippingForm
컴포넌트에서 작업하는 사람이 불필요한 의존성을 추가하는 것을 방지할 수 있다.커스텀 훅은 더 나은 패턴으로 마이그레이션하는데에 도움을 준다.
- 시간이 지남에 따라, React 팀의 목표는 더 구체적인 문제에 대한 더 구체적인 솔루션을 제공함으로써 “앱에서 Effect의 수를 최소한으로 줄이는 것”
- Effect를 커스텀 훅으로 감싸면 이러한 솔루션이 제공될 때 코드를 더 쉽게 업그레이드 가능하다.
- design system과 비슷하게 앱의 컴포넌트에서 공통된 관용구를 추출하여 커스텀 훅으로 만드는 것이 도움이 될 수 있다.
- 컴포넌트의 코드가 의도에 집중, raw Effect를 자주 사용하는 것을 방지할 수 있다.
출처(리액트 공식문서) : https://react.dev/learn/reusing-logic-with-custom-hooks