전체내용 : Hook의 규칙
1. Hook의 규칙
Hook은 JavaScript함수이다. 하지만 Hook을 사용할 때는 두 가지 규칙을 준수해야 한다.
React에서는 규칙을 자동으로 강제하기 위해 linter 플러그인 을 제공한다.
1) 규칙
(1) 최상위(at the Top Level)에서만 Hook을 호출해야 한다.
반복문, 조건문 혹은 중첩된 함수 내에서 Hook을 호출하면 안 된다. 항상 React함수의 최상위에서 Hook을 호출해야 한다.
→ 컴포넌트가 렌더링 될 때마다 동일한 순서로 Hook이 호출되는 것이 보장된다.
→ React가 useState
와 useEffect
가 여러 번 호출되는 중에도 Hook의 상태를 올바르게 유지할 있게 해준다.
(2) 오직 React 함수 내에서 Hook을 호출해야 한다.
일반적인 JavaScript 함수에서 Hook을 호출하면 안 된다.
가능한 호출 방법은 React함수 컴포넌트에서 호출, Custom Hook에서 호출 두 가지이다.
→ 이 규칙을 지키면 컴포넌트의 모든 상태 관련 로직을 소스코드에서 명확하게 보이도록 할 수 있다.
2) ESLint 플러그인
위의 두 규칙을 강제하는 eslint-plugin-react-hooks
라는 ESLint 플러그인이 있다.
Create React App
에 기본적으로 포함되어 있다.
> npm install eslint-plugin-react-hooks --save-dev
// ESLint 설정 파일
{
"plugins": [
// ...
"react-hooks"
],
"rules": {
// ...
"react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
"react-hooks/exhaustive-deps": "warn" // Checks effect dependencies
}
}
3) 규칙 설명
React가 특정 state가 어떤 useState
호출에 해당하는지 알 수 있는 이유는 React가 Hook이 호출되는 순서에 의존하기 때문이다. 모든 렌더링에서 Hook의 호출 순서는 같기 때문에 올바르게 동작할 수 있다.
하지만, Hook을 조건문 안에서 호출한다면 (첫 번째 규칙을 깸) Hook을 건너뛰는 경우가 생길 수 있고 Hook을 호출하는 순서가 달라지게 된다. 이에 따라 건너뛴 Hook 다음에 호출되는 Hook이 순서가 하나씩 밀리면서 버그를 발생시킨다.
⇒ 컴포넌트 최상위에서만 Hook을 호출하는 이유
만약 조건부로 effect를 실행하길 원한다면, 조건문을 Hook 내부에 넣으면 된다.
useEffect(function persistForm() {
// 👍 더 이상 첫 번째 규칙을 어기지 않습니다
if (name !== '') {
localStorage.setItem('formData', name);
}
});
2. 자신만의 Hook 만들기
자신만의 Hook을 만들면 컴포넌트 로직을 함수로 뽑아내어 재사용 할 수 있다.
1) 상태 관련 로직을 컴포넌트에서 공유하는 방법
여기서 예시의 목표는
FriendSatus
와FriendListItem
컴포넌트에 중복되어있는 로직을 제거하는 것
(1) 사용자 정의 Hook 추출하기
두 개의 자바스크립트 함수에서 같은 로직을 공유하고자 할 때는 또 다른 함수로 분리한다. Hook또한 함수이므로 보통의 함수와 같은 방법으로 사용한다.
사용자 정의 Hook은 이름이 use
로 시작하는 자바스크립트 함수다. 사용자 Hook은 다른 Hook을 호출할 수 있다. (이름은 반드시 use로 시작해야한다. Hook규칙이 적용되는지 파악하기 위함)
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
// ...
return isOnline; // 온라인 상태 여부 반환
}
(2) 사용자 정의 Hook 이용하기
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);
...
}
function FriendListItem(props) {
const isOnline = useFriendStatus(props.friend.id);
...
}
- 사용자 정의 Hook은 React의 특별한 기능이라기 보다는 기본적으로 Hook의 디자인을 따르는 관습이다.
- 사용자 정의 Hook의 이름은
use
로 시작되어야 한다. 이를 따르지 않으면 특정한 함수가 그 안에서 Hook을 호출하는지 알 수 없기 때문에 Hook 규칙 위반 여부를 자동으로 체크할 수 없다. - 같은 Hook을 사용하는 두 개의 컴포넌트는 state를 공유하지 않는다. 사용자 정의 Hook은 상태 관련 로직을 재사용하는 매커니즘일 뿐, Hook을 사용할 때마다 그 안의
state
와effect
는 완전히 독립적이다. - 각각의 Hook에 대한 호출은 서로 독립된 state를 받는다.
useFriendStatus
를 직접적으로 호출하기 때문에 React의 관점에서 컴포넌트에서useState
와useEffect
를 호출한 것과 다름없다. 또한, 하나의 컴포넌트 안에서useState
와useEffect
를 여러 번 호출할 수 있고 이들은 모두 완전히 독립적이다.
2) 기타
(1) Hook에서 Hook으로 정보 전달하기
Hook은 함수이기 때문에 Hook사이에서도 정보를 전달할 수 있다.
// 현재 선택된 친구의 ID를 recipientID state 변수에 저장하고
// 사용자가 <select> 선택기에 있는 다른 친구를 선택하면 이를 업데이트하는 코드
// -> 지금 선택되어있는 친구의 온라인 상태여부를 알 수 있다.
const [recipientID, setRecipientID] = useState(1);
const isRecipientOnline = useFriendStatus(recipientID);
return (
<>
<Circle color={isRecipientOnline ? 'green' : 'red'} />
<select
value={recipientID}
onChange={e => setRecipientID(Number(e.target.value))}
>
...
useState
Hook호출은 recipientID
state 변수의 최신값을 돌려주기 때문에 이를 useFriendStatus
Hook에 인수로 보낼 수 있다.