관리 메뉴

꿈꾸는 개발자

React Hook 규칙 정리 본문

React.js

React Hook 규칙 정리

rickysin 2023. 2. 2. 15:43

Hook의 규칙(16.8 version에 추가된 기능) 

https://www.npmjs.com/package/eslint-plugin-react-hooks

 

eslint-plugin-react-hooks

ESLint rules for React Hooks. Latest version: 4.6.0, last published: 8 months ago. Start using eslint-plugin-react-hooks in your project by running `npm i eslint-plugin-react-hooks`. There are 7139 other projects in the npm registry using eslint-plugin-rea

www.npmjs.com

  • 위 링크는 Hook 규칙을 강제하는 linter 플러그인 

최상위(at the Top Level)에서만 Hook을 호출하라! 

  • 반복문, 조건문, 중첩함수 내에서 Hook 사용X + 최상위에서만 Hook 호출 => 컴포넌트가 렌더링 될 때마다 항상 동일한 순서로 Hook이 호출됨 => useState/useEffect가 여러 번 호출되더라도, react에서 상태를 유지할 수 있게 됨!

오직 React 함수 내에서 Hook을 호출하라!(JS함수 내에 Hook 호출X)

  • React 함수 component에서 Hook을 호출
  • Custom Hook에서 Hook을 호출하라!

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
  }
}

여러 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;
  });

  // ...
}
  • 위와 같이 여러 Hook을 사용할 경우 리액트는 Hook이 호출되는 순서를 통해 특정 state가 어떤 useState 호출에 해당하는지 분별한다.
 
// ------------
// 첫 번째 렌더링
// ------------
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의 호출 순서가 렌더링 중 동일하면 => React는 지역적인 state를 각 Hook과 연동할 수 있다! BUT, 조건문 안에서 Hook을 사용할 경우 => 
  // 🔴 조건문에 Hook을 사용함으로써 첫 번째 규칙을 깼습니다
  if (name !== '') {
    useEffect(function persistForm() {
      localStorage.setItem('formData', name);
    });
  }
  • 첫 번째 렌더링에서는 true일 것이다 => 하지만, 사용자가 다음 렌더링에서 form을 초기화 => 조건이 false로 변경된다 => (즉 form을 초기화할 경우 위 조건은 false이기 때문에 실행되지 않는다!) => 결국, Hook의 호출 순서가 달라지게 된다(렌더링에 마다)
useState('Mary')           // 1. name state 변수를 읽습니다. (인자는 무시됩니다)
// useEffect(persistForm)  // 🔴 Hook을 건너뛰었습니다!
useState('Poppins')        // 🔴 2 (3이었던). surname state 변수를 읽는 데 실패했습니다.
useEffect(updateTitle)     // 🔴 3 (4였던). 제목을 업데이트하기 위한 effect가 대체되는 데 실패했습니다
  • React에서는 두 번째 Hook의 호출이 persistForm effect와 동일할 것으로 예상하고 시작 => 하지만 위에 설명한 이유로 생략된다 => 따라서, PASS된 Hook부터 하나씩 호출이 밀리면서 버그가 발생하게 된다!
  • 이것이 바로 Component 최상위 즉 조건문 내부가 아닌, component scope level?에서 Hook을 사용하는 이유이다 => 만약 조건부로 effect을 실행하고 싶으면 Hook 내부에 작성해라!
  useEffect(function persistForm() {
    // 👍 더 이상 첫 번째 규칙을 어기지 않습니다
    if (name !== '') {
      localStorage.setItem('formData', name);
    }
  });

핵심 요약: 

- Hook은 기본적으로 반복문, 조건문, 중첩함수 내에 사용X

- Hook은 함수 내에서만 호출

-이런 고민을 하기 귀찮다면, 리액트에서 제공하는 linter 플러그인을 install하자


출처: 

https://ko.reactjs.org/docs/hooks-rules.html

 

Hook의 규칙 – React

A JavaScript library for building user interfaces

ko.reactjs.org