#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]
:
- Компонент монтируется и
useEffect
запускается. setData(result)
будет асинхронно обновлятьdata
значение и запускать повторный запуск.- Во время
useEffect
повторного выполнения будет запущен снова, так какdata
сравнение ссылок не будет успешно завершено. - Повторите 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;