일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 코딩테스트
- 리액트 프로젝트
- 파이썬 replace
- tanstack query
- 리액트 공식 문서
- useEffect
- Next 팀 프로젝트
- REACT
- 한글 공부 사이트
- 내일배움캠프 최종 프로젝트
- 프로그래머스
- 리액트 공식문서
- 파이썬 반복문
- 파이썬 for in
- 리액트
- 파이썬 enumerate
- 내일배움캠프
- 자바스크립트
- typeScript
- useState
- 리액트 훅
- JavaScript
- 파이썬 딕셔너리
- React Hooks
- 파이썬 slice
- 내일배움캠프 프로젝트
- 타입스크립트
- 파이썬 for
- 타입스크립트 리액트
- 내배캠 프로젝트
- Today
- Total
sohyeon kim
[React] 리액트 공식문서 정리 4(5) : 탈출구 - 커스텀 Hook 으로 로직 재사용하기 본문
💡 리액트 공식문서 정리 : 탈출구 - 커스텀 Hook 으로 로직 재사용하기
React 는 useState, useContext, useEffect 와 같은 Hook 들이 내장되어 있다.
때로는 데이터를 가져오거나 사용자가 온라인 상태인지 여부를 추적하는 등 좀 더 구체적인 목적을 가진 Hook 을 원할 수 있다.
이를 위해 애플리케이션의 필요에 알맞는 자신만의 커스텀 Hook 을 생성할 수 있다.
앱이 성장함에 따라 이미 작성한 커스텀 Hook 을 재사용할 수 있으므로 직접 작성하는 Effect 의 수가 줄어들 것이다.
1. 커스텀 Hook : 컴포넌트간 로직 공유하기
네트워크에 크게 의존하는 앱을 개발 중이라고 가정하자. 사용자가 앱을 사용하던 중 네트워크가 갑자기 사라진다면, 사용자에게 경고하고 싶을 것이다. 이런 경우 컴포넌트에는 다음 두 가지가 필요하다.
- 네트워크가 온라인 상태인지 아닌지 추적하는 하나의 state
- 전역 online, offline 이벤트를 구독하고, 이에 맞춰 state 를 업데이트하는 effect
이 두 가지 요소는 컴포넌트가 네트워크 상태와 동기화되도록 한다.
import { useState, useEffect } from 'react';
export default function StatusBar() {
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 <h1>{isOnline ? '✅ 온라인' : '❌ 연결 안 됨'}</h1>;
}
네트워크를 껐다 키면, 그에 따라 StatusBar 가 업데이트 된다.
이제 다른 컴포넌트에서 같은 로직을 또 사용한다고 해보자. 네트워크가 꺼졌을 때, '저장' 대신 '재연결 중..' 을 보여주는 비활성화된 저장 버튼을 구현하고 싶다고 가정해 보자.
이를 구현하기 위해, 앞서 사용한 isOnline state 와 effect 를 SaveButton 안에 복사할 수 있다.
import { useState, useEffect } from 'react';
export default function SaveButton() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
//...
}, []);
function handleSaveClick() {
console.log('✅ 진행사항 저장됨');
}
return (
<button disabled={!isOnline} onClick={handleSaveClick}>
{isOnline ? '진행사항 저장' : '재연결 중...'}
</button>
);
}
네크워크를 끄고 켰을 때, 컴포넌트는 잘 동작하지만 로직이 중복되는 점이 아쉽다.
두 컴포넌트가 다른 UI 를 표시하면서, 두 컴포넌트 사이의 로직을 재사용하고 싶다.
1️⃣ 컴포넌트로부터 커스텀 Hook 추출하기
useState, useEffect 와 비슷한 내장된 useOnlineStatus Hook 이 있다고 상상해 보자.
그럼 두 컴포넌트를 단순화할 수 있고, 중복을 제거할 수 있다.
function StatusBar() {
const isOnline = useOnlineStatus();
return <h1>{isOnline ? '✅ 온라인' : '❌ 연결 안 됨'}</h1>;
}
function SaveButton() {
const isOnline = useOnlineStatus();
function handleSaveClick() {
console.log('✅ 진행사항 저장됨');
}
return (
<button disabled={!isOnline} onClick={handleSaveClick}>
{isOnline ? '진행사항 저장' : '재연결 중...'}
</button>
);
}
이제 useOnlineStatue 함수를 정의하고, 앞서 작성한 컴포넌트들의 중복 코드를 바꾸자.
함수의 마지막에 isOnline 을 반환하면 컴포넌트가 그 값을 읽을 수 있게 해준다.
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;
}
여기서 중요한 건, 두 컴포넌트 내부 코드가 어떻게 그것을 하는지(브라우저 이벤트 구독하기)보다
그들이 무엇을 하려는지(온라인 state 사용하기)에 대해 설명하고 있다는 점이다.
커스텀 Hook 을 만들어 낼 때, 브라우저 API 나 외부 시스템과 소통하는 방법과 같은 불필요한 세부 사항은 숨길 수 있으며,
컴포넌트의 코드는 목적만을 나타낼 뿐 실행 방법에 대해선 나타내지 않게 된다.
2️⃣ Hook 의 이름은 항상 use 로 시작해야 한다.
React 애플리케이션은 여러 컴포넌트로 만들어진다. 컴포넌트들은 내장되거나 직접 작성한 Hook 으로 만들어진다.
본인만의 Hook 을 만들려고 할 때, 다음의 작명 규칙을 준수해야 한다.
- React 컴포넌트의 이름은 항상 대문자로 시작해야 한다.(e.g. StatusBar)
또한 컴포넌트는 JSX 처럼 어떻게 보이는지 React 가 알 수 있는 무언가를 반환해야 한다. - Hook 의 이름은 use 뒤에 대문자로 시작해야 한다. (e.g. useState(내장 훅) or useOnlineStatus(커스텀 훅))
Hook 들은 어떤 값이든 반환할 수 있다.
이런 규칠들은 컴포넌트를 볼 때, 어디에 state, effect 및 다른 React 기능들이 '숨어' 있는지 알 수 있게 해준다.
예시로, 만약 컴포넌트 안에 getColor() 라는 함수를 보았을 때, 해당 함수의 이름이 use 로 시작하지 않으므로 함수 안에 React state 가 있을 수 없다는 것을 확신할 수 있다. 반대로 useOnlineStatus() 함수의 경우 높은 확률로 내부에 다른 Hook 을 사용하고 있을 수 있다.
🧐 렌더링 중 호출되는 모든 함수는 use 접두사로 시작해야 하나?
아니다! Hook 을 호출하지 않는 함수는 Hook 일 필요가 없다.
함수가 어떤 Hook 도 호출하지 않는다면, use 를 이름 앞에 작성하는 것을 피하라. 대신 use 없이 일반적인 함수로 작성하자.
예시로 useSorted 가 Hook 을 호출하지 않는다면 getSorted 로 변경할 수 있다.
// 🔴 안 좋은 예시 : Hook을 사용하고 있지 않는 Hook.
function useSorted(items) {
return items.slice().sort();
}
// ✅ 좋은 예시 : Hook을 사용하지 않는 일반 함수.
function getSorted(items) {
return items.slice().sort();
}
다음 예시는 조건문 뿐만 아니라 어디든 일반 함수를 사용할 수 있다는 것을 보여준다.
function List({ items, shouldSort }) {
let displayedItems = items;
if (shouldSort) {
// ✅ getSorted()가 Hook이 아니기 때문에 조건에 따라 호출할 수 있습니다.
displayedItems = getSorted(items);
}
// ...
}
적어도 하나의 Hook 을 내부에서 사용한다면 반드시 함수 앞에 use 를 작성해야 한다. (그리고 이 자체로 Hook 이 된다.)
// ✅ 좋은 예시 : Hook을 사용하는 Hook
function useAuth() {
return useContext(Auth);
}
기술적으로 이 점이 React 에 의해 강요되진 않지만, 참고하는 것이 좋다.
예시로, 지금 당장은 함수에서 어떤 Hook 도 사용하지 않지만, 추후 Hook 을 호출할 계획이 있다면 use 를 앞에 붙여 두는 것이 가능하다.
지금이든 나중이든 Hook 을 내부에서 사용할 계획이 없다면, Hook 으로 만들지 말 것!
// ✅ 좋은 예시 : 추후에 다른 Hook을 사용할 가능성이 있는 Hook
function useAuth() {
// TODO: 인증이 수행될 때 해당 코드를 useContext(Auth)를 반환하는 코드로 바꾸기
return TEST_USER;
}
3️⃣ 커스텀 Hook 은 state 그 자체를 공유하는게 아닌 state 저장 로직을 공유하도록 한다.
위 예시에선, 네트워크를 껐다 켰을 때 양쪽 컴포넌트가 함께 업데이트되었다.
하지만 그렇다고 해서 isOnline state 변수가 두 컴포넌트간 공유되었다고 생각하면 안된다.
function StatusBar() {
const isOnline = useOnlineStatus();
// ...
}
function SaveButton() {
const isOnline = useOnlineStatus();
// ...
}
// 중복된 부분을 걷어내기 전에도 동일하게 동작한다.
function StatusBar() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
// ...
}, []);
}
function SaveButton() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
// ...
}, []);
}
완전히 독립적인 두 state 변수와 effect 가 있음을 확인할 수 있다. 그들은 우리가 동일한 외부 변수(네트워크의 연결 state)를 동기화했기 때문에 같은 시간에 같은 값을 가지고 있을 뿐이다. 이걸 더 잘 표현하는 다른 예시를 살펴보자.
import { useState } from 'react';
export default function Form() {
const [firstName, setFirstName] = useState('Mary');
const [lastName, setLastName] = useState('Poppins');
function handleFirstNameChange(e) {
setFirstName(e.target.value);
}
function handleLastNameChange(e) {
setLastName(e.target.value);
}
return (
<>
<label>
First name:
<input value={firstName} onChange={handleFirstNameChange} />
</label>
<label>
Last name:
<input value={lastName} onChange={handleLastNameChange} />
</label>
<p><b>Good morning, {firstName} {lastName}.</b></p>
</>
);
}
각각의 폼 입력에 반복되는 로직이 있다.
- state 가 존재한다. (firstName 과 lastName)
- 변화를 다루는 함수가 존재한다. (handleFirstNameChange 와 handleLastNameChange)
- 해당 입력에 대한 value 와 onChange 의 속성을 지정하는 JSX 가 존재한다.
useFormInput 커스텀 Hook 을 통해 반복되는 로직을 추출할 수 있다.
export function useFormInput(initialValue) {
const [value, setValue] = useState(initialValue);
function handleChange(e) {
setValue(e.target.value);
}
const inputProps = {
value: value,
onChange: handleChange
};
return inputProps;
}
export default function Form() {
const firstNameProps = useFormInput('Mary');
const lastNameProps = useFormInput('Poppins');
return (
<>
<label>
First name:
<input {...firstNameProps} />
</label>
<label>
Last name:
<input {...lastNameProps} />
</label>
<p><b>Good morning, {firstNameProps.value} {lastNameProps.value}.</b></p>
</>
);
}
여기서 value 라고 불리는 state 변수가 한 번만 정의된다는 것을 기억하자.
이와 달리 Fom 컴포넌트는 useFormInput 을 두 번 호출한다.
위 예시는 왜 두 개의 다른 state 변수를 정의하는 식으로 동작하는 지 보여준다.
커스텀 Hook 은 우리가 state 그 자체가 아닌 state 저장 로직을 공유하도록 해준다. 같은 Hook 을 호출하더라도 각각의 Hook 호출은 완전히 독립되어 있다. 이것이 위의 두 코드가 완전히 같은 이유다. 커스텀 Hook 을 추출하기 전과 후가 동일하다.
대신 여러 컴포넌트 간 state 자체를 공유할 필요가 있다면, state 를 위로 올려 전달하자.
2. Hook 사이에 상호작용하는 값 전달하기
커스텀 Hook 안의 코드는 컴포넌트가 리렌더링될 때마다 다시 돌아갈 것이다.
➡️ 커스텀 Hook 이 컴포넌트처럼 순수해야 하는 이유이다! 커스텀 Hook 을 컴포넌트 본체의 한 부분이라고 생각하자!
커스텀 Hook 이 컴포넌트와 함께 리렌더링된다면, 항상 가장 최신의 props 와 state 를 전달받을 것이다.
아래 채팅방 예시에서 서버 URL 이나 채팅방을 바꾼다고 생각해 보자.
import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';
import { showNotification } from './notifications.js';
export default function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useEffect(() => {
const options = {
serverUrl: serverUrl,
roomId: roomId
};
const connection = createConnection(options);
connection.on('message', (msg) => {
showNotification('New message: ' + msg);
});
connection.connect();
return () => connection.disconnect();
}, [roomId, serverUrl]);
return (
<>
<label>
Server URL:
<input value={serverUrl} onChange={e => setServerUrl(e.target.value)} />
</label>
<h1>Welcome to the {roomId} room!</h1>
</>
);
}
serverUrl 이나 roomId 를 변경할 때, effect 는 변화에 '반응'하며 재동기화한다. effect 의 의존성이 변경될 때마다 채팅방을 재연결하는 콘솔 메시지를 보낼 수 있다. 이제 Effect 코드를 커스텀 Hook 안에 넣어보자.
export function useChatRoom({ serverUrl, roomId }) {
useEffect(() => {
const options = {
serverUrl: serverUrl,
roomId: roomId
};
const connection = createConnection(options);
connection.connect();
connection.on('message', (msg) => {
showNotification('New message: ' + msg);
});
return () => connection.disconnect();
}, [roomId, serverUrl]);
}
ChatRoom 컴포넌트가 내부 동작이 어떻게 동작하는지 걱정할 필요 없이 커스텀 Hook 을 호출할 수 있게 해준다.
export default function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useChatRoom({
roomId: roomId,
serverUrl: serverUrl
});
return (
<>
<label>
Server URL:
<input value={serverUrl} onChange={e => setServerUrl(e.target.value)} />
</label>
<h1>Welcome to the {roomId} room!</h1>
</>
);
}
매우 간단해졌다! (그런데도 똑같이 동작한다!)
매번 ChatRoom 이 리렌더링될 때마다, Hook 에 최신 roomId 와 serverUrl 값을 넘겨준다.
➡️ 리렌더링 이후 값이 달라지는지 여부에 관계없이 effect 가 재연결하는 이유이다.
만약 오디오 또는 비디오 처리 소프트웨어를 작업해 본 적이 있다면, 이처럼 Hook 을 연결하는 것이 시각적 혹은 청각적 효과를 연결하는 것을 떠오르게 할 것이다. 이게 바로 useState 의 결과를 useChatRoom 의 입력으로 '넣어주는 것' 과 같다.
3. 언제 커스텀 Hook 을 사용해야 하는가?
모든 자잘한 중복 코드들을 커스텀 Hook 으로 분리할 필요는 없다. 예시로, 하나의 useState 를 감싸기 위한 useFormInput 을 분리하는 것은 불필요하다.
하지만 effect 를 사용하든 사용하지 않든, 커스텀 Hook 안에 그것을 감싸는 게 좋은지 아닌지를 고려하자. effect 를 자주 쓸 필요가 없을지 모른다. 만약 effect 를 사용한다면, 그건 외부 시스템과 동기화한다던가 React 가 내장하지 않은 API 를 위해 무언가를 하는 등 'React 에서 벗어나기' 위함일 것이다. 커스텀 Hook 으로 감싸는 것은 목적을 정확하게 전달하고 어떻게 데이터가 그것을 통해 흐르는지 알 수 있게 해준다. 예시로 두 가지 목록을 보여주는 ShippingForm 컴포넌트를 살펴보자. 하나는 도시의 목록을 보여주고, 다른 하나는 선택된 도시의 구역 목록을 보여준다.
function ShippingForm({ country }) {
const [cities, setCities] = useState(null);
// 이 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);
// 이 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]);
// ...
이 코드들은 반복적이지만, effect 들을 따로 분리하는 것이 옳다. 이들은 다른 두 가지(도시, 구역)를 동기화하므로 하나의 effect 로 통합시킬 필요가 없다. 대신 ShippingForm 컴포넌트를 useData 라는 커스텀 Hook 을 통해 공통된 로직을 추출할 수 있다.
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;
}
이제 ShippingForm 컴포넌트 내부의 effect 들을 useData 로 교체할 수 있다.
function ShippingForm({ country }) {
const cities = useData(`/api/cities?country=${country}`);
const [city, setCity] = useState(null);
const areas = useData(city ? `/api/areas?city=${city}` : null);
// ...
커스텀 Hook 을 추출하는 것은 데이터의 흐름을 명확하게 해준다. url 을 입력하고 data 를 받는다.
useData 안의 effect 를 '숨김으로써' 다른 사람이 ShippingForm 컴포넌트에 불필요한 의존성을 추가하는 것을 막을 수 있다.
시간이 지나면 앱의 대부분 effect 들은 커스텀 Hook 안에 있을 것이다.
❗️ 커스텀 Hook 이 구체적인 고급 사용 사례에 집중하도록 하기 🔗
커스텀 Hook 의 이름은 누가 보아도 Hook 이 무슨 일을 하고, 무엇을 props 로 받고, 무엇을 반환하는지 알 수 있도록 매우 명확해야 한다. 또한 외부 시스템과 동기화할 땐, 이름에 좀 더 기술적이고 해당 시스템을 특정하는 용어를 사용하는 것이 좋다.
- ✅ useData(url)
- ✅ useImpressionLog(eventName, extraData)
- ✅ useChatRoom(options)
- ✅ useMediaQuery(query)
- ✅ useSocket(url)
- ✅ useIntersectionObserver(ref, options)
커스텀 Hook 이 구체적인 고급 사용 사례에 집중할 수 있도록 하자. useEffect API 그 자체를 위한 대책이나 편리하게 감싸는 용도로 동작하는 커스텀 '생명 주기' Hook 을 생성하거나 사용하는 것을 피하자. 용도를 명확히 하자.
- 🔴 useMount(fn)
- 🔴 useEffectOnce(fn)
- 🔴 useUpdateEffect(fn)
예시로, 위 useMount Hook 은 코드가 '마운트 시'에만 동작하는 것을 확인하기 위해 만들어졌다.
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
// 🔴 안 좋은 예 : 커스텀 "생명 주기" Hook을 사용
useMount(() => {
const connection = createConnection({ roomId, serverUrl });
connection.connect();
post('/analytics/event', { eventName: 'visit_chat' });
});
// ...
}
// 🔴 안 좋은 예 : 커스텀 "생명 주기" Hook을 생성
function useMount(fn) {
useEffect(() => {
fn();
}, []); // 🔴 React Hook useEffect은 'fn'의 의존성을 갖고 있지 않음.
}
useMount 와 같은 커스텀 '생명 주기' Hook 은 전형적인 React 와 맞지 않는다. 이 코드 예시는 문제가 있지만(roomId 나 serverUrl 의 변화에 반응하지 않음), 린터는 오직 직접적인 useEffect 호출만 체크하기에 경고를 보내지 않는다. 린터는 Hook 에 대해 모르고 있다.
🤓 커스텀 Hook 은 더 나은 패턴으로 변경할 수 있도록 도와준다.
effect 는 탈출구이다. 'React 에서 벗어나'는 것이 필요할 때나 사용 시에 괜찮은 내장된 해결 방법이 없는 경우, 사용한다.
React 팀의 목표는 더 구체적인 문제에 더 구체적인 해결 방법을 제공해 앱에 있는 effect 의 숫자를 점차 최소한으로 줄이는 것이다.
커스텀 Hook 으로 effect 를 감싸는 것은 이런 해결 방법들이 가능해질 때 코드를 쉽게 업그레이드할 수 있게 해준다.
import { useState, useEffect } from 'react';
export 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;
}
위 예시에서 useOnlineStatus 는 한 쌍의 useState 와 useEffect 와 함께 실행된다. 이것은 가장 좋은 해결 방법은 아니다.
이 방법이 고려하지 못한 어려 예외 상황이 존재하는데, 예시로 이건 컴포넌트가 마운트됐을 때 isOnline 이 이미 true 라고 가정한다.
하지만 이것은 네트워크가 이미 꺼졌을 때 틀린 가정이 된다. 이런 상황을 확인하기 위해 브라우저 navigator.onLine API 를 사용할 수도 있지만 이걸 직접적으로 사용하게 되면 초기 HTML 을 생성하기 위한 서버에선 동작하지 않는다. 그러므로 코드는 보완되어야 한다.
React 18 은 이런 모든 문제를 신경 써주는 useSyncExternalStore 라고 불리는 섬세한 API 를 포함한다.
아래는 새 API 의 장점을 가지고 다시 쓰인 useOnlineStatus 이다.
import { useSyncExternalStore } from 'react';
function subscribe(callback) {
window.addEventListener('online', callback);
window.addEventListener('offline', callback);
return () => {
window.removeEventListener('online', callback);
window.removeEventListener('offline', callback);
};
}
export function useOnlineStatus() {
return useSyncExternalStore(
subscribe,
() => navigator.onLine, // 클라이언트의 값을 받아오는 방법
() => true // 서버의 값을 받아오는 방법
);
}
어떻게 이 변경을 하기 위해 다른 컴포넌트들을 변경하지 않아도 되는지 알아보자.
function StatusBar() {
const isOnline = useOnlineStatus();
// ...
}
function SaveButton() {
const isOnline = useOnlineStatus();
// ...
}
커스텀 Hook 으로 effect 를 감싸는 것이 종종 유용한 이유는 다음과 같다.
- 매우 명확하게 effect 로 주고받는 데이터 흐름을 만들 때
- 컴포넌트가 effect 의 정확한 실행보다 목적에 집중하도록 할 때
- React 가 새 기능을 추가할 때, 다른 컴포넌트의 변경 없이 이 effect 를 삭제할 수 있을 때
디자인 시스템과 마찬가지로, 앱의 컴포넌트에서 일반적인 관용구를 추출해 커스텀 Hook 으로 만드는 것이 도움이 될 수 있다.
그럼 컴포넌트의 코드가 의도에 집중할 수 있고, effect 를 자주 작성하지 않아도 된다.
➕
- 하나의 Hook에서 다른 Hook으로 반응형 값을 전달할 수 있고, 값은 최신 상태로 유지된다.
- 커스텀 Hook 을 통해 받는 이벤트 핸들러는 effect 로 감싸야 한다. 🔗
- 코드의 경계를 선택하는 방법과 위치는 우리가 결정할 수 있다.
- 가끔 Hook 이 필요하지 않을 수도 있다.
🔗 https://ko.react.dev/learn/reusing-logic-with-custom-hooks
커스텀 Hook으로 로직 재사용하기 – React
The library for web and native user interfaces
ko.react.dev