Эффект использования, вызывающий бесконечный цикл или получение ошибок

#reactjs #react-hooks #use-effect

#reactjs #реагирующие крючки #использование-эффект

Вопрос:

Я пытаюсь изучить реагирующие хуки. Я создаю простое новостное приложение, которое использует API NY times.

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

Когда я использую isLoading, он работает, но затем я получаю сообщение об ошибке «localhost /: 1 Непроверенная среда выполнения.lastError: порт сообщения закрыт до получения ответа». и «localhost /: 1 Ответ на обработку ошибок: ошибка типа: не удается прочитать свойство ‘level’ неопределенного»

main.js

 import React, { useEffect, useState } from "react";
import { nyTimesApi } from "../services/Api";
const Main = () => {
  const [isLoading, setIsLoading] = useState(true);
  const [data, setData] = useState([]);

  const fetchData = async () => {
    const result = await nyTimesApi();
    setData(result);
    setIsLoading(false);
    console.log(data.results);
  };
 
  useEffect(() => {
    fetchData();
  }, [isLoading]);
  return <div className="main">work</div>;
};
export default Main;

 

Я также получаю предупреждение при использовании isLoading в терминале, в котором говорится: «У React Hook useEffect отсутствует зависимость: ‘fetchData’. Либо включите его, либо удалите массив зависимостей react-hooks / исчерпывающий-deps »

Что я делаю не так?

Комментарии:

1. Ваш код в полном порядке. Не могли бы вы поделиться более исходным кодом или воспроизвести его в CodeSanbox?

2. Вы не можете console.log указывать изменения в следующей строке, изменения состояния являются асинхронными. Регистрируйте состояние в основном теле функции, чтобы увидеть последнее значение состояния. Ваш эффект может быть буквально useEffect(fetchData, []); таким, чтобы он выполнялся один раз при монтировании

3. Массив зависимостей должен быть пустым, если вы хотите, чтобы функция выполнялась только при начальной загрузке. Я почти уверен, что вы можете игнорировать предупреждение о необходимости ‘fetchData’ в массиве зависимостей.

Ответ №1:

Второй аргумент to useEffect — это массив переменных, которые запускают функцию в useEffect, вызываемую при каждом их изменении.

У вас есть [isLoading] в качестве второго аргумента для useEffect и обновления значения this внутри fetchData() . Это приведет к тому, что триггер useEffect будет повторяться снова и снова и снова.

Если вы хотите, чтобы useEffect вызывался только один раз (аналогично componentDidMount в компонентах на основе классов), тогда вам нужно указать пустой массив в качестве второго аргумента.

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

Комментарии:

1. Хотя он точен для проверки ссылок на объекты, массивы, функции и т. Д., Объяснение цикла не является точным, поскольку isLoading это логическое значение. Логическое значение будет сравнением значений и в этом случае должно вызвать только одно дополнительное неожиданное выполнение useEffect . В вопросе указано, что бесконечный цикл был вызван использованием data объекта вместо isLoading в массиве зависимостей.

Ответ №2:

Бесконечный цикл вызван сочетанием использования setData(result) и [data] :

  1. Компонент монтируется и useEffect запускается.
  2. setData(result) будет асинхронно обновлять data значение и запускать повторный запуск.
  3. Во время useEffect повторного выполнения будет запущен снова, так как data сравнение ссылок не будет успешно завершено.
  4. Повторите 2-3 раза.

Предупреждение «React Hook useEffect имеет отсутствующую зависимость» в некоторой степени говорит само за себя.

Использование внешней (для useEffect ) переменной, которая не включена в массив зависимостей, может означать, что значение переменной изменяется и useEffect не будет повторно запущено или что значение может не соответствовать ожидаемому значению.

Ниже приведен пример того, как исходный фрагмент может быть исправлен:

 import React, { useEffect, useState } from "react";
import { nyTimesApi } from "../services/Api";

const Main = () => {
  const [isLoading, setIsLoading] = useState(true);
  const [data, setData] = useState([]);
 
  useEffect(() => {
    // Create function inside useEffect so that the function is only
    // created everytime the useEffect runs and not every render.
    const fetchData = async () => {
        const result = await nyTimesApi();
        setData(result);
        setIsLoading(false);

        // setData will update state asynchronously.
        // Log the value stored instead.
        console.log(result.results);
    };

    //Run data fetching function.
    fetchData();

  }, 
  // Both of these are set functions created by useState and will
  // not change for the life of the component, but adding them to
  // the dependency array will make your linter happy.

  // Do not need to check isLoading as it is always true on component
  // mount.
  [setData, setIsLoading]);

  return <div className="main">work</div>;
};

export default Main;