Як виправити відсутні попередження про залежність при використанні useEffect React Hook?


179

З React 16.8.6 (це було добре в попередній версії 16.8.3), я отримую цю помилку, коли намагаюся запобігти нескінченний цикл на запит на отримання

./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

Мені не вдалося знайти рішення, яке зупинить нескінченний цикл. Я хочу триматися подалі від використання useReducer(). Я знайшов цю дискусію https://github.com/facebook/react/isissue/14920, де можливим рішенням є You can always // eslint-disable-next-line react-hooks/exhaustive-deps if you think you know what you're doing.не впевненість у тому, що я роблю, тому я ще не намагався її здійснити.

У мене є поточна установка React hook useEffect працює безперервно назавжди / нескінченний цикл, і єдиний коментар, про useCallback()який я не знайомий.

Як я зараз використовую useEffect()(який я хочу запустити лише один раз на початку, подібно до componentDidMount())

useEffect(() => {
    fetchBusinesses();
  }, []);
const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };

Відповіді:


192

Якщо ви не використовуєте метод fetchBususiness ніде, крім ефекту, ви можете просто перемістити його в ефект і уникнути попередження

useEffect(() => {
    const fetchBusinesses = () => {
       return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
  fetchBusinesses();
}, []);

Якщо ви використовуєте fetchBususiness за межами візуалізації, ви повинні зауважити дві речі

  1. Чи є якась проблема з вами, яка не переходить fetchBusinessesяк метод, коли він використовується під час монтажу із закритим закриттям?
  2. Чи залежить ваш метод від деяких змінних, які він отримує від закритого закриття? Це не для вас.
  3. Після кожного візуалізації, fetchBusiness буде створено заново, і, отже, передача його в useEffect спричинить проблеми. Тож спочатку ви повинні запам’ятати fetchBususiness, якщо ви перейшли до масиву залежностей.

Підсумовуючи це, я б сказав, що якщо ви використовуєте fetchBusinessesпоза useEffectвами, ви можете відключити правило, використовуючи // eslint-disable-next-line react-hooks/exhaustive-depsінакше, ви можете перемістити метод всередину useEffect

Щоб вимкнути правило, ви б його написали так

useEffect(() => {
   // other code
   ...

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

14
Я чудово використав рішення, яке ви окреслили. Ще одне рішення, яке я використовував ще там, де було інше налаштування useCallback(). Так, наприклад: const fetchBusinesses= useCallback(() => { ... }, [...]) і useEffect()виглядатиме так:useEffect(() => { fetchBusinesses(); }, [fetchBusinesses]);
russ

1
@russ, ви маєте рацію, вам потрібно буде запам'ятати fetchBusiness за допомогою useCallback, якщо ви перейдете до масиву залежностей
Shubham

Було б непогано, якби ви показали, куди слід ставити оператор eslint-disabled. Я думав, що це буде вище useEffect
user210757

1
використовувати, // eslint-disable-next-line react-hooks/exhaustive-depsщоб пояснити лінтові, що ваш код правильний - це як злом. Я сподіваюся, що вони знайдуть інше рішення, щоб зробити лінійку більш розумною, щоб виявити, коли аргумент не є обов’язковим
Олів'є Боассе

1
@TapasAdhikary, так, ви можете мати функцію асинхронізації у useEffect, вам просто потрібно написати це по-іншому. Перевірте stackoverflow.com/questions/53332321/…
Shubham

75
./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

Це не помилка JS / React, а попередження eslint (eslint-plugin-react-hooks).

Це говорить вам, що гачок залежить від функції fetchBusinesses, тому вам слід сприймати це як залежність.

useEffect(() => {
  fetchBusinesses();
}, [fetchBusinesses]);

Це може призвести до виклику функції кожного візуалізації, якщо функція оголошена у компоненті типу:

const Component = () => {
  /*...*/

  //new function declaration every render
  const fetchBusinesses = () => {
    fetch('/api/businesses/')
      .then(...)
  }

  useEffect(() => {
    fetchBusinesses();
  }, [fetchBusinesses]);

  /*...*/
}

тому що кожен раз функція переосмислюється новою посиланням

Правильний спосіб цього матеріалу:

const Component = () => {
  /*...*/

  // keep function reference
  const fetchBusinesses = useCallback(() => {
    fetch('/api/businesses/')
      .then(...)
  }, [/* additional dependencies */]) 

  useEffect(() => {
    fetchBusinesses();
  }, [fetchBusinesses]);

  /*...*/
}

або просто визначення функції в useEffect

Детальніше: https://github.com/facebook/react/isissue/14920


14
Це призводить до нової помилкиLine 20: The 'fetchBusinesses' function makes the dependencies of useEffect Hook (at line 51) change on every render. Move it inside the useEffect callback. Alternatively, wrap the 'fetchBusinesses' definition into its own useCallback() Hook
russ

1
рішення чудово, і якщо на функції ви змінюєте інший стан, вам потрібно додати залежності, щоб уникнути іншої несподіваної поведінки
cesarlarsson

57

Ви можете встановити його безпосередньо як useEffectзворотний дзвінок:

useEffect(fetchBusinesses, [])

Він запуститься лише один раз, тому переконайтесь, що всі функції функції встановлені правильно (так само, як і використання componentDidMount/componentWillMount...)


Редагувати 21.02.2020

Просто для повноти:

1. Використовуйте функцію як useEffectзворотний дзвінок (як вище)

useEffect(fetchBusinesses, [])

2. Оголосити функцію всередині useEffect()

useEffect(() => {
  function fetchBusinesses() {
    ...
  }
  fetchBusinesses()
}, [])

3. Пам'ятайте с useCallback()

У цьому випадку, якщо у вас є функції у вашій функції, вам доведеться включити їх до useCallbackмасиву залежностей, і це знову запустить, useEffectякщо парами функції зміниться. Крім того, це багато плит котла ... Тому просто передайте функцію безпосередньо useEffectяк в 1. useEffect(fetchBusinesses, []).

const fetchBusinesses = useCallback(() => {
  ...
}, [])
useEffect(() => {
  fetchBusinesses()
}, [fetchBusinesses])

4. Вимкніть попередження eslint

useEffect(() => {
  fetchBusinesses()
}, []) // eslint-disable-line react-hooks/exhaustive-deps

2
Я люблю тебе ... Ця відповідь настільки повна!
Nick09

8

Рішення також надається шляхом реагування, вони користуються порадами, useCallbackякі повернуть пам'ятну версію вашої функції:

Функція "fetchBususiness" змінює залежності використанняEffect Hook (у рядку NN) на кожному візуалізації. Щоб виправити це, увімкніть визначення "fetchBususiness" у власне використанняCallback () Hook reaction-гачки / вичерпні-deps

useCallbackпростий у використанні, оскільки він має ту саму підпис, що useEffectі різниця полягає в тому, що useCallback повертає функцію. Це виглядатиме так:

 const fetchBusinesses = useCallback( () => {
        return fetch("theURL", {method: "GET"}
    )
    .then(() => { /* some stuff */ })
    .catch(() => { /* some error handling */ })
  }, [/* deps */])
  // We have a first effect thant uses fetchBusinesses
  useEffect(() => {
    // do things and then fetchBusinesses
    fetchBusinesses(); 
  }, [fetchBusinesses]);
   // We can have many effect thant uses fetchBusinesses
  useEffect(() => {
    // do other things and then fetchBusinesses
    fetchBusinesses();
  }, [fetchBusinesses]);

2

Ці застереження дуже корисні для пошуку компонентів, які не оновлюються постійно: https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of- залежності .

Однак якщо ви хочете видалити попередження протягом свого проекту, ви можете додати це до конфігурації eslint:

  {
  "plugins": ["react-hooks"],
  "rules": {
    "react-hooks/exhaustive-deps": 0
    }
  }

1

Ця стаття є хорошим букварем для отримання даних за допомогою гачків: https://www.robinwieruch.de/react-hooks-fetch-data/

По суті, включіть визначення функції вибору всередину useEffect:

useEffect(() => {
  const fetchBusinesses = () => {
    return fetch("theUrl"...
      // ...your fetch implementation
    );
  }

  fetchBusinesses();
}, []);

1

Ви можете видалити масив типу 2-го аргументу, []але fetchBusinesses()також буде викликано кожне оновлення. Якщо ви хочете, ви можете додати IFзаяву до fetchBusinesses()реалізації.

React.useEffect(() => {
  fetchBusinesses();
});

Інший - це реалізація fetchBusinesses()функції поза вашим компонентом. Просто не забувайте передавати будь-які аргументи залежності на ваш fetchBusinesses(dependency)дзвінок, якщо такі є.

function fetchBusinesses (fetch) {
  return fetch("theURL", { method: "GET" })
    .then(res => normalizeResponseErrors(res))
    .then(res => res.json())
    .then(rcvdBusinesses => {
      // some stuff
    })
    .catch(err => {
      // some error handling
    });
}

function YourComponent (props) {
  const { fetch } = props;

  React.useEffect(() => {
    fetchBusinesses(fetch);
  }, [fetch]);

  // ...
}

0

Насправді попередження дуже корисні, коли ви розробляєте гачки. але в деяких випадках це може забити вас голкою. особливо, коли вам не потрібно слухати зміни залежностей.

Якщо ви не хочете ставити fetchBusinessesвсередині залежності від гака, ви можете просто передати його як аргумент зворотного дзвінка гачка і встановити головне fetchBusinessesяк значення за замовчуванням для нього, як це

useEffect((fetchBusinesses = fetchBusinesses) => {
   fetchBusinesses();
}, []);

Це не найкраща практика, але вона може бути корисною в деяких випадках.

Також, як писав Shubnam, ви можете додати код нижче, щоб сказати ESLint ігнорувати перевірку вашого гака.

// eslint-disable-next-line react-hooks/exhaustive-deps

0

Я хочу лише запустити [ fetchBusinesses] один раз на початку, подібно доcomponentDidMount()

Ви можете fetchBusinessesповністю витягнути свій компонент:

const fetchBusinesses = () => { // or pass some additional input from component as args
  return fetch("theURL", { method: "GET" }).then(n => process(n));
};

const Comp = () => {
  React.useEffect(() => {
    fetchBusinesses().then(someVal => {
      // ... do something with someVal
    });
  }, []); // eslint warning solved!
  return <div>{state}</div>;
};

Це не тільки забезпечить просте рішення та вирішить вичерпне попередження про деп. fetchBusinessтепер можна перевірити краще і полегшує Comp, оскільки він знаходиться в області модуля поза деревом React.

Переміщення fetchBusinessesназовні добре працює тут, оскільки ми зможемо прочитати лише початкові реквізити та стан із компонента так чи інакше завдяки усталеній області закриття ( []dep in useEffect).

Як опустити залежності функції

  • Переміщення функції всередині ефекту
  • Перемістити функцію за межами компонента - (ми використовуємо цей)
  • Функція виклику під час надання та нехай useEffectзалежить від цього значення (чиста обчислювальна функція)
  • Додайте функцію для ефектів deps і оберніть її useCallbackяк крайній спосіб

Щодо інших рішень:

Потягнення fetchBusinessesвсередину useEffect()насправді не допомагає, якщо ви маєте доступ до іншого стану в ньому. eslint все ще скаржиться: Codesandbox .

Я б також утримався від вичерпних вичерпних коментарів deps-deps. Просто забути їх просто, коли ви робите реконструкцію та капітальний ремонт ваших залежностей.


0
const [mount, setMount] = useState(false)
const fetchBusinesses = () => { 
   //function defination
}
useEffect(() => {
   if(!mount) {
      setMount(true);
      fetchBusinesses();
   }
},[fetchBusinesses]);

Це рішення досить просте, і вам не потрібно переосмислювати попередження. Просто тримайте прапор, щоб перевірити, встановлений чи ні компонент.


0

ви намагаєтеся таким чином

const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };

і

useEffect(() => {
    fetchBusinesses();
  });

це робота для вас. Але моя пропозиція спробувати цей спосіб також працює для вас. Це краще, ніж раніше. Я використовую такий спосіб:

useEffect(() => {
        const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
        fetchBusinesses();
      }, []);

якщо ви отримуєте дані на базі конкретного ідентифікатора, тоді додайте зворотний дзвінок useEffect, [id]то він не може показувати вам попередження React Hook useEffect has a missing dependency: 'any thing'. Either include it or remove the dependency array


-4

просто відключити eslint для наступного рядка;

useEffect(() => {
   fetchBusinesses();
// eslint-disable-next-line
}, []);

таким чином ви використовуєте його так само, як компонент зробив монтаж (викликався один раз)

оновлено

або

const fetchBusinesses = useCallback(() => {
 // your logic in here
 }, [someDeps])

useEffect(() => {
   fetchBusinesses();
// no need to skip eslint warning
}, [fetchBusinesses]); 

fetchпідприємства будуть називатися кожного разу, коли деякіDeps змінюватимуться


замість того, щоб вимикати, просто це: [fetchBusinesses]автоматично видалить попередження і це вирішило проблему для мене.
rotimi-best

7
@RotimiBest - це спричиняє нескінченне повторне відображення, як описано у питанні
user210757

Я насправді зробив це таким чином в одному з моїх проектів деякий час тому, і це не дало нескінченного циклу. Я перевірю ще раз.
rotimi-best

@ user210757 Зачекайте, але чому це спричинить нескінченний цикл, це не так, як ви встановлюєте стан після отримання даних із сервера. Якщо ви оновлювали стан, просто запишіть умову if перед викликом функції, useEffectяка перевіряє, чи стан порожній.
rotimi-best

@ rotimi-best був ahwile, оскільки я прокоментував, але я б сказав, що функція відновлюється кожен раз, тому ніколи не буде однаковою, тому завжди буде повторно відображатися, якщо ви не перейдете в тіло useEffect або useCallback
user210757
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.