웹개발/리액트

[React] Hooks

tjcldnjs 2024. 3. 23. 18:45

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을 호출한다.