[React] Hooks
Hook
Hook은 함수 컴포넌트에서 React state와 생명주기 기능을 연동(hook into)할 수 있게 해주는 함수이다. Hook은 class 없이 React를 사용할 수 있게 해준다.
useState, useEffect와 같이 React에서 자체 지원하는 내장 Hook이 있으며, 직접 custom Hook을 만드는 것도 가능하다.
Hook 사용 규칙
Hook을 사용할 때에는 두 가지 규칙을 준수해야 한다.
1. 최상위에서만 Hook을 호출해야 한다.
반복문, 조건문, 중첩된 함수 내에서 Hook을 실행하면 안된다.
React에서는 한 컴포넌트에서 State나 Effect Hook을 여러 개 사용할 수도 있다.
function Form() {
// 1. name이라는 state 변수를 사용
const [name, setName] = useState('Mary');
// 2. Effect를 사용해 폼 데이터를 저장
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
// 3. surname이라는 state 변수를 사용
const [surname, setSurname] = useState('Poppins');
// 4. Effect를 사용해서 제목을 업데이트
useEffect(function updateTitle() {
document.title = name + ' ' + surname;
});
// ...
}
이 때, React는 Hook이 호출되는 순서로 어떤 state가 어떤 useState 호출에 해당하는지 판단한다. 모든 렌더링에서 Hook의 호출 순서가 같다면 올바르게 동작할 수 있다.
// ------------
// 첫 번째 렌더링
// ------------
useState('Mary') // 1. 'Mary'라는 name state 변수 선언
useEffect(persistForm) // 2. 폼 데이터를 저장하기 위한 effect 추가
useState('Poppins') // 3. 'Poppins'라는 surname state 변수 선언
useEffect(updateTitle) // 4. 제목을 업데이트하기 위한 effect 추가
// -------------
// 두 번째 렌더링
// -------------
useState('Mary') // 1. name state 변수 읽기(인자는 무시됨)
useEffect(persistForm) // 2. 폼 데이터를 저장하기 위한 effect가 대체됨
useState('Poppins') // 3. surname state 변수 읽기(인자는 무시됨)
useEffect(updateTitle) // 4. 제목을 업데이트하기 위한 effect가 대체됨
// ...
하지만 Hook을 조건문 안에서 호출하여 호출 순서가 달라지면 문제가 발생한다.
// 🔴 조건문에 Hook을 사용함으로써 첫 번째 규칙 위반
if (name !== '') {
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
}
name !== ' ' 조건이 첫 렌더링에서는 true기 때문에 useEffect가 동작한다. 하지만 그 다음 렌더링에서 폼을 초기화하면서 조건이 false가 된다.
useState('Mary') // 1. name state 변수 읽기 (인자는 무시됨)
// useEffect(persistForm) // 🔴 Hook을 건너뜀
useState('Poppins') // 🔴 2 (3이었던). surname state 변수 읽기 실패
useEffect(updateTitle) // 🔴 3 (4였던). 제목 업데이트를 위한 effect 대체 실패
React는 이전 렌더링처럼 컴포넌트 내에서 두 번째 Hook 호출이 persistForm effect일 것이라 예상했지만 그렇지 않았다. 건너뛴 Hook 다음부터 호출되는 Hook의 순서가 밀리며 버그를 발생시킨다.
조건부로 effect를 실행하려면 조건문을 Hook 내부에 넣어 사용할 수 있다.
useEffect(function persistForm() {
if (name !== '') {
localStorage.setItem('formData', name);
}
});
2. React 함수 컴포넌트 혹은 custom Hook 내에서만 Hook을 호출해야 한다.
Hook을 일반적인 JavaScript 함수에서는 호출하면 안되며, 아래와 같이 호출할 수있다.
- React 함수 컴포넌트에서 Hook을 호출한다.
- Custom Hook에서 Hook을 호출한다.