React: exhustive-deps Eslint 규칙 이해하기

이예빈·2022년 11월 12일
5

JavaScript

목록 보기
26/26
post-thumbnail

"react-hooks/exhaustive-deps" 규칙은 효과 후크에 누락된 종속성이 있을 때 경고를 띄운다. 경고를 없애려면 useEffect 내부의 함수 또는 변수 선언을 이동하고 렌더링할 때마다 변경되는 배열 및 개체를 메모화하거나 규칙을 비활성화해야 한다.

🚨 경고가 나타나는 경우의 예


import React, {useEffect, useState} from 'react';

export default function App() {
  const [address, setAddress] = useState({country: '', city: ''});

  // 👇️ 객체와 배열은 참조자료형이기 때문에 내부의 값이 변하지 않더라도 렌더링이 될 때마다 다른 값인 것으로 간주된다.
  const obj = {country: 'Germany', city: 'Hamburg'};

  useEffect(() => {
    setAddress(obj);
    console.log('useEffect called');

    // ⛔️ React Hook useEffect has a missing dependency: 'obj'.
    // Either include it or remove the dependency array. eslintreact-hooks/exhaustive-deps
  }, []);

  return (
    <div>
      <h1>Country: {address.country}</h1>
      <h1>City: {address.city}</h1>
    </div>
  );
}

문제는 useEffect 내부에서 obj 변수 를 사용 하고 있지만 종속성 배열에는 포함되지 않았다는 것이다.

오류에 대한 가장 확실한 해결책은 종속성 배열에 obj를 추가하는 것이다.

그러나 이 경우 객체와 배열이 JavaScript에서 참조로 비교되기 때문에 오류가 발생하게 된다.

obj는 리렌더링 될 때 동일한 키-값 쌍을 가진 객체이지만 매번 메모리의 다른 reference를 가리키므로 동등성 검사에 실패하고 무한 렌더링이 발생한다.

🎯 해결 방법


1. 주석 추가

이 때 경고를 피하는 방법중 하나는 한 줄 또는 전체 파일에 대한 eslint 규칙을 비활성화 하는 것이다.

import React, {useEffect, useState} from 'react';

export default function App() {
  const [address, setAddress] = useState({country: '', city: ''});

  const obj = {country: 'Germany', city: 'Hamburg'};

  useEffect(() => {
    setAddress(obj);
    console.log('useEffect called');

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <div>
      <h1>Country: {address.country}</h1>
      <h1>City: {address.city}</h1>
    </div>
  );
}

종속성 배열 윗 줄에 추가된 주석은 한 줄에 대한 react-hooks/exhausting-deps 규칙을 비활성화한다.

💡 후크에 빈 배열이 두 번째 매개변수로 전달 되면 useEffect구성 요소가 마운트될 때만 호출된다.

2. useEffect 내부에서 선언

다른 해결책은 변수나 함수의 선언을 useEffect 내부로 이동하는 것이다.

import React, {useEffect, useState} from 'react';

export default function App() {
  const [address, setAddress] = useState({country: '', city: ''});

  useEffect(() => {
    // 👇️ 참조 자료형의 데이터를 useEffect 내부에서 선언하기
    const obj = {country: 'Germany', city: 'Hamburg'};

    setAddress(obj);
    console.log('useEffect called');
  }, []);

  return (
    <div>
      <h1>Country: {address.country}</h1>
      <h1>City: {address.city}</h1>
    </div>
  );
}

useEffect 내부에서 개체에 대한 변수를 선언하였다.

hook이 더 이상 외부 개체에 대한 종속성을 가지지 않기 때문에 이렇게 하면 경고가 제거된다.

3. 컴포넌트 밖에서 선언

드물게 사용할 수 있지만 알고 있으면 좋은 또 다른 솔루션은 함수 또는 변수 선언을 구성 요소 밖으로 옮기는 것이다.

import React, {useEffect, useState} from 'react';

// 👇️ 함수나 변수의 선언을 컴포넌트 외부로 옮기기
const obj = {country: 'Germany', city: 'Hamburg'};

export default function App() {
  const [address, setAddress] = useState({country: '', city: ''});

  useEffect(() => {
    setAddress(obj);
    console.log('useEffect called');
  }, []);

  return (
    <div>
      <h1>Country: {address.country}</h1>
      <h1>City: {address.city}</h1>
    </div>
  );
}

이렇게 하면 구성 요소가 다시 렌더링 될 때마다 변수가 다시 생성되지 않기 때문에 도움이 될 수 있다.

변수는 매 렌더링마다 메모리의 동일한 위치를 가리키므로 useEffect 종속성 배열에서 변수를 추적할 필요가 없다.

4. useMemo 사용

이를 대체할 수 있는 솔루션은 memoized 값을 얻기 위해 useMemo hook을 사용하는 것이다.

import React, {useMemo, useEffect, useState} from 'react';

export default function App() {
  const [address, setAddress] = useState({country: '', city: ''});

  // 👇️ memoized된 값 가져오기
  const obj = useMemo(() => {
    return {country: 'Germany', city: 'Hamburg'};
  }, []);

  useEffect(() => {
    setAddress(obj);
    console.log('useEffect called');

    // 👇️ 안전하게 종속성 배열에 추가할 수 있다.
  }, [obj]);

  return (
    <div>
      <h1>Country: {address.country}</h1>
      <h1>City: {address.city}</h1>
    </div>
  );
}

렌더링 간에 변경되지 않는 메모화된 값을 얻기 위해 useMemo hook을 사용했다.

💡useMemo는 메모할 값과 종속성 배열을 매개변수로 반환하는 함수를 사용한다. 이는 종속성 중 하나가 변경된 경우에만 메모된 값을 다시 계산한다.

5. useCallback 사용

함수로 작업하는 경우 useCallback를 사용하여 렌더링 간에 변경되지 않는 메모화된 콜백을 가져올 수 있다.

import React, {useMemo, useEffect, useState, useCallback} from 'react';

export default function App() {
  const [address, setAddress] = useState({country: '', city: ''});

  // 👇️ memoized된 콜백함수를 가져온다.
  const sum = useCallback((a, b) => {
    return a + b;
  }, []);

  // 👇️ memoized된 값을 가져온다.
  const obj = useMemo(() => {
    return {country: 'Germany', city: 'Santiago'};
  }, []);

  useEffect(() => {
    setAddress(obj);
    console.log('useEffect called');

    console.log(sum(100, 100));

    // 👇️ 안전하게 종속성 배열에 추가할 수 있다.
  }, [obj, sum]);

  return (
    <div>
      <h1>Country: {address.country}</h1>
      <h1>City: {address.city}</h1>
    </div>
  );
}

useCallback은 인라인 콜백 함수와 종속성 배열을 사용하고 종속성 중 하나가 변경된 경우에만 변경되는 메모화된 버전의 콜백함수를 반환한다.

reference


https://bobbyhadz.com/blog/react-hooks-exhaustive-deps

profile
temporary potato

0개의 댓글