С 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(). Я нашел это обсуждение [ESLint] Отзыв о правиле lint 'exhaustive-deps' #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
});
};
И запрос функции, чтобы React улучшили useEffect API, чтобы избежать этой проблемы, четко отделив триггеры эффектов от зависимостей эффектов: github.com/facebook/реагировать/вопросы/22132 Поскольку Next.js включил линтинг по умолчанию, это предупреждение должно появляться миллион раз в день во всем мире, это должно прекратиться. как-то.
Согласитесь, это совершенно неясно из официальной документации. Такая библиотека, как React, не нуждается в форумах и сообщениях в блогах, чтобы заставить ее работать.





Эта статья является хорошим пособием по извлечению данных с помощью хуков: https://www.robinwieruch.de/react-hooks-fetch-data/.
По сути, включите определение функции выборки внутри useEffect:
useEffect(() => {
const fetchBusinesses = () => {
return fetch("theUrl"...
// ...your fetch implementation
);
}
fetchBusinesses();
}, []);
./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
Это не ошибка JavaScript/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 the function reference
const fetchBusinesses = useCallback(() => {
fetch('/api/businesses/')
.then(...)
}, [/* Additional dependencies */])
useEffect(() => {
fetchBusinesses();
}, [fetchBusinesses]);
/*...*/
}
Или просто определите функцию в useEffect.
Подробнее: [ESLint] Отзыв о правиле lint 'exhaustive-deps' #14920
решение в порядке, и если в функции вы изменяете другое состояние, вам нужно добавить зависимости, чтобы избежать другого неожиданного поведения
Если вы не используете метод 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();
}, []);
Однако, если вы используете fetchBusinesses вне рендеринга, вы должны обратить внимание на две вещи.
fetchBusinesses в качестве метода, когда он используется во время монтирования с закрывающим закрытием?Подводя итог, я бы сказал, что если вы используете fetchBusinesses вне useEffect, вы можете отключить правило, используя // eslint-disable-next-line react-hooks/exhaustive-deps, в противном случае вы можете переместить метод внутрь useEffect.
Чтобы отключить правило, вы должны написать его как
useEffect(() => {
// other code
...
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
Я использовал решение, которое вы хорошо изложили. Еще одно решение, которое я использовал в другом месте из-за другой настройки, было useCallback(). Так, например: const fetchBusinesses= useCallback(() => { ... }, [...]) и useEffect() будет выглядеть так: useEffect(() => { fetchBusinesses(); }, [fetchBusinesses]);
@russ, вы правы, вам нужно будет запомнить fetchBusiness с помощью useCallback, если вы хотите передать его в массив зависимостей
Было бы неплохо, если бы вы показали, куда поставить оператор eslint-disable. Я думал, что это будет выше useEffect
использование // eslint-disable-next-line react-hooks/exhaustive-deps для объяснения линтеру, что ваш код правильный, похоже на взлом. Я ожидаю, что они найдут другое решение, чтобы сделать линтер достаточно умным, чтобы определять, когда аргумент не является обязательным.
Как насчет асинхронной функции?
@TapasAdhikary, асинхронную функцию также можно вызвать из useEffect
Спасибо @ShubhamKhatri, я понял. Мой вопрос заключался в том, что если мы хотим использовать само определение функции внутри useEffect(), а моя функция является ансинхронной функцией, которая была вне useEffect(), как получить это внутри? Функция aync получила ключевое слово await, которое, я полагаю, не может быть размещено как есть.
@TapasAdhikary, да, у вас может быть асинхронная функция в useEffect, вам просто нужно написать ее по-другому. Пожалуйста, проверьте stackoverflow.com/questions/53332321/…
«Если есть какие-либо проблемы, если вы не передадите fetchBusinesses как метод и что он используется во время монтирования с его закрывающим закрытием, не будет никаких проблем». -> Это предложение неясно.
Я могу предвидеть, что во многих проектах везде будет много кода, где будет писаться эта строка отключения. Я чувствую, что это ошибка, которую нужно решить разработчикам.
Но это не работает, когда мы используем async и await внутри функции useEffect. Поэтому я попытался поставить его снаружи. Но это все еще не работает!
Я думаю, что принятым ответом должен быть ответ на «r g» ниже. ИМО - плохая практика комментировать ошибки ворса, когда есть другое жизнеспособное решение. + вам нужно доверять следующему разработчику, который редактирует этот код, чтобы сначала удалить комментарий.
Я ненавижу это, когда приходится перемещать функцию внутри хука. Должен быть лучший способ.
линтер все еще тупой сегодня, и если вы хотите поведение, подобное componentDidMount, при использовании внешних переменных (необходимо, чтобы некоторые, но не все из них вызывали повторную визуализацию в случае их изменения), вы получите это предупреждение независимо от того, что вы делаете.... в по крайней мере, я не смог найти решение в Интернете
@rayaqin Do может отключить линтер для этой строки, если вы абсолютно уверены, что делаете правильно.
это не заставит предупреждение браузера исчезнуть, только предупреждение в IDE
Привет, ребята, я отправил запрос функции для дополнительного параметра «триггеры» в useEffect, чтобы различать зависимости, которые должны запускать повторный запуск эффекта, и зависимости, которые просто запускают обновление обратного вызова эффекта, но не запускают его. Я хотел бы получить отзыв об этом: github.com/facebook/реагировать/вопросы/22132 (связанная песочница кода: codeandbox.io/s/fancy-sea-zj5e4).
И люди удивляются, почему мы, «ленивые» разработчики, склонны игнорировать предупреждения, лол.
Просто отключите ESLint для следующей строки;
useEffect(() => {
fetchBusinesses();
// eslint-disable-next-line
}, []);
Таким образом, вы используете его так же, как компонент, который монтируется (вызывается один раз).
обновлен
или
const fetchBusinesses = useCallback(() => {
// Your logic in here
}, [someDeps])
useEffect(() => {
fetchBusinesses();
// No need to skip the ESLint warning
}, [fetchBusinesses]);
fetchBusinesses будет вызываться каждый раз при изменении некоторые Депсы.
вместо отключения просто сделайте следующее: [fetchBusinesses] автоматически удалит предупреждение, и это решило проблему для меня.
@RotimiBest - это приводит к бесконечному повторному рендерингу, как описано в вопросе.
На самом деле я сделал это в одном из своих проектов некоторое время назад, и это не привело к бесконечному циклу. Я проверю еще раз, хотя.
@ user210757 Подождите, но почему это вызовет бесконечный цикл, ведь вы не устанавливаете состояние после получения данных с сервера. Если вы обновляли состояние, просто напишите условие if перед вызовом функции в useEffect, которая проверяет, пусто ли состояние.
@rotimi-best был ahwile с тех пор, как я прокомментировал, но я бы сказал, что функция воссоздается каждый раз, поэтому никогда не бывает одинаковой, поэтому всегда будет перерисовываться, если вы не перейдете в тело useEffect или useCallback
см. это полезное обсуждение: github.com/facebook/создать-реагировать-приложение/issues/6880
Что вы подразумеваете под "точно так же, как компонент монтировался" (кажется непонятным)? Пожалуйста, ответьте с помощью редактирование (изменение) вашего ответа, а не здесь, в комментариях (без "Редактировать:", "Обновить:" или подобное - ответ должен выглядеть так, как будто он был написан сегодня).
Вы можете удалить второй массив типов аргументов [], но 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]);
// ...
}
удаление скобок массива зависимостей вызвало проблему бесконечного повторного рендеринга в компоненте, где у меня есть форма!
Решение также дается React. Они советуют вам использовать useCallback, который вернет версию вашей функции запоминать:
The 'fetchBusinesses' function makes the dependencies of the useEffect Hook (at line NN) change on every render. To fix this, wrap the 'fetchBusinesses' definition into its own useCallback() Hook react-hooks/exhaustive-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 that uses fetchBusinesses
useEffect(() => {
// Do things and then fetchBusinesses
fetchBusinesses();
}, [fetchBusinesses]);
// We can have many effects that use fetchBusinesses
useEffect(() => {
// Do other things and then fetchBusinesses
fetchBusinesses();
}, [fetchBusinesses]);
В моем случае этот крючок useCallBack решил мою проблему. Подробности см. на странице документация.
Редактировать 20.09.2021
There are very good options for state management libs if you are creating a new app or have enough flexibility. Check out Recoil.
Просто для полноты:
useEffectuseEffect(fetchBusinesses, [])
useEffect()useEffect(() => {
function fetchBusinesses() {
...
}
fetchBusinesses()
}, [])
useCallback()В этом случае, если у вас есть зависимости в вашей функции, вам придется включить их в массив зависимостей useCallback, и это снова вызовет useEffect, если параметры функции изменятся. Кроме того, это много шаблонного... Так что просто передайте функцию прямо в useEffect, как в 1. useEffect(fetchBusinesses, []).
const fetchBusinesses = useCallback(() => {
...
}, [])
useEffect(() => {
fetchBusinesses()
}, [fetchBusinesses])
As suggested by https://stackoverflow.com/a/61183162/5552686
useEffect((fetchBusinesses = fetchBusinesses) => {
fetchBusinesses();
}, []);
Создайте собственный хук и вызывайте его, когда вам нужно запустить функцию только один раз. Может быть чище. Вы также можете вернуть обратный вызов для сброса повторного запуска «инициализации», когда это необходимо.
// customHooks.js
const useInit = (callback, ...args) => {
const [mounted, setMounted] = useState(false)
const resetInit = () => setMounted(false)
useEffect(() => {
if (!mounted) {
setMounted(true);
callback(...args);
}
},[mounted, callback]);
return [resetInit]
}
// Component.js
return ({ fetchBusiness, arg1, arg2, requiresRefetch }) => {
const [resetInit] = useInit(fetchBusiness, arg1, arg2)
useEffect(() => {
resetInit()
}, [requiresRefetch, resetInit]);
Disabling warnings should be your last resort, but when you do, better do it inline and explicitly, because future developers may be confused or create unexpected bugs without knowing linting is off
useEffect(() => {
fetchBusinesses()
}, []) // eslint-disable-line react-hooks/exhaustive-deps
Исходный ответ
Вы можете установить его непосредственно как обратный вызов useEffect:
useEffect(fetchBusinesses, [])
Он сработает только один раз, поэтому убедитесь, что все зависимости функции установлены правильно (так же, как при использовании componentDidMount/componentWillMount...).
Можно ли отключить предупреждение эслинта?
Я предпочитаю отключать предупреждения в крайнем случае, потому что будущие разработчики могут запутаться или создать неожиданные ошибки, не зная, что линтинг отключен.
В последней реакции. Если я объявляю состояние, а компонент использует useEffect, отображается предупреждение. Таким образом, в этом случае решения 1, 2 и 3 неприменимы, так как я хочу, чтобы они запускались только один раз. Что я должен делать?
@RohanShenoy Если я правильно понимаю, вы хотите использовать состояние внутри useEffect, это совершенно другая ситуация. В этом случае вы можете только отключить предупреждение, потому что теперь у вас есть вторая внешняя зависимость
useEffect(fetchBusinesses, []) выдаст «Ошибка типа: функция эффекта не должна возвращать ничего, кроме функции, которая используется для очистки»., так как fetchBusinesses возвращает обещание.
@EmileBergeron Это правда с реализацией OP! Они могли бы просто удалить return из fetchBusinesses, так как это не требуется.
@EmileBergeron useEffect не следует передавать асинхронной функции, это можно сделать следующим образом: useEffect( () => { asyncFunc() } ,[])
// eslint-disable-line react-hooks/exhaustive-deps работает как шарм.
Первое предложение на удивление не устраняет предупреждение
Это БОЛЬШЕ НЕ РАБОТАЕТ!!!!
Есть ли разница, если функция асинхронная? Это то, что я делаю, это правильно? const loadAgents = useCallback(async ()=> { try { ... const response = await axios.post(...); setAgentsData(response.data); } catch (e) { ... } finally { .. . } }, []); useEffect(() => { if (...) loadAgents(); }, [uiState.selectedSwitch, loadAgents]);
Обратите внимание, я должен был поставить // eslint-disable-next-line react-hooks/exhaustive-deps, а не // eslint-disable-line react-hooks/exhaustive-deps
@LewyBlue это потому, что вы добавили комментарий над в строку зависимостей
Эти предупреждения очень полезны для поиска компонентов, которые не обновляются последовательно: Безопасно ли исключать функции из списка зависимостей?.
Однако, если вы хотите удалить предупреждения по всему проекту, вы можете добавить это в свою конфигурацию ESLint:
{
"plugins": ["react-hooks"],
"rules": {
"react-hooks/exhaustive-deps": 0
}
}
На самом деле предупреждения очень полезны при разработке с помощью хуков. Но в некоторых случаях это может вас уколоть. Особенно, когда вам не нужно прослушивать изменение зависимостей.
Если вы не хотите помещать fetchBusinesses в зависимости хука, вы можете просто передать его в качестве аргумента обратному вызову хука и установить основной fetchBusinesses в качестве значения по умолчанию для него следующим образом:
useEffect((fetchBusinesses = fetchBusinesses) => {
fetchBusinesses();
}, []);
Это не лучшая практика, но в некоторых случаях это может быть полезно.
Кроме того, как Шубхам написал, вы можете добавить приведенный ниже код, чтобы указать ESLint игнорировать проверку вашего хука.
// eslint-disable-next-line react-hooks/exhaustive-deps
const [mount, setMount] = useState(false)
const fetchBusinesses = () => {
// Function definition
}
useEffect(() => {
if (!mount) {
setMount(true);
fetchBusinesses();
}
},[fetchBusinesses, mount]);
Это решение довольно простое, и вам не нужно переопределять предупреждения ESLint. Просто сохраните флаг, чтобы проверить, смонтирован ли компонент или нет.
И вы будете делать это каждый раз, когда вам понадобится componentDidMount?
Затем это пометит вас, что вам нужно добавить mount в качестве зависимости от 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
});
};
а также
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();
}, []);
Если вы получаете данные на базе определенного id, то добавьте в callback useEffect [id]. Тогда он не может показать вам предупреждение
React Hook useEffect has a missing dependency: 'any thing'. Either include it or remove the dependency array
Просто передайте функцию в качестве аргумента в массиве useEffect...
useEffect(() => {
functionName()
}, [functionName])
Что ж, если вы хотите посмотреть на это по-другому, вам просто нужно знать, какие опции есть у React, которые не являются exhaustive-deps. Одна из причин, по которой вы не должны использовать функцию замыкания внутри эффекта, заключается в том, что при каждом рендеринге он будет воссоздаваться/уничтожаться снова.
Таким образом, в хуках есть несколько методов React, которые считаются стабильными и неисчерпаемыми, где вам не нужно применять зависимости useEffect и, в свою очередь, не будет нарушать правила взаимодействия react-hooks/exhaustive-deps. Например, вторая возвращаемая переменная useReducer или useState, которая является функцией.
const [,dispatch] = useReducer(reducer, {});
useEffect(() => {
dispatch(); // Non-exhausted - ESLint won't nag about this
}, []);
Так что, в свою очередь, вы можете сосуществовать со всеми вашими внешними зависимостями с вашими текущими зависимостями в вашей функции редуктора.
const [,dispatch] = useReducer((current, update) => {
const { foobar } = update;
// Logic
return { ...current, ...update };
}), {});
const [foobar, setFoobar] = useState(false);
useEffect(() => {
dispatch({ foobar }); // non-exhausted `dispatch` function
}, [foobar]);
Это предупреждение возникает, если переменные, которые вы используете внутри useEffect, определены внутри компонента или переданы компоненту в качестве реквизита. Поскольку вы определили fetchBusinesses() внутри того же компонента, вы должны передать его в массив зависимостей.
Но если вы импортировали fetchBusinesses(), а затем использовали его внутри useEffect, вам не нужно было бы добавлять его в массив зависимостей. Вот как мы на самом деле настраиваем наши приложения Редукс: мы всегда импортируем наших создателей действий и запускаем их внутри useEffect, не добавляя их в массив зависимостей.
То же самое верно и для useMemo.
вы все равно получите предупреждение, даже если импортируете функцию из своего магазина. Потому что ваши функции будут переданы объекту props через вызов mapDispatchToProps или с использованием второго аргумента тега connect. connect(mapStateToProps, {fetchBusinesses})(Component)
@osmancakirio Вы нашли решение предупреждения в этом случае? У меня та же проблема...
@ndtreviv Я провел рефакторинг компонентов, чтобы теперь вместо тега подключения использовались редукционные хуки. затем я передаю функцию отправки массиву зависимостей. Это также рекомендуется разработчиками redux, поскольку, как они говорят, это безопасно, потому что ссылка на функцию отправки почти никогда не меняется.
В моем случае это предупреждение было связано с моей локальной переменной organization, и когда я помещал organization в массив зависимостей, useEffect извлекался бесконечно. Поэтому, если у вас есть проблемы, подобные моей, используйте useEffect с массивом зависимостей и разделите:
Потому что, если у вас есть несколько вызовов API, которые изменяют состояние, он вызывает useEffect несколько раз.
От:
const { organization } = useSelector(withOrganization)
const dispatch = useDispatch()
useEffect(() => {
dispatch(getOrganization({}))
dispatch(getSettings({}))
dispatch(getMembers({}))
}, [dispatch, organization])
К:
const { organization } = useSelector(withOrganization)
const dispatch = useDispatch()
useEffect(() => {
dispatch(getOrganization({}))
dispatch(getSettings({}))
}, [dispatch, organization])
useEffect(() => {
dispatch(getMembers({}))
}, [dispatch])
Вы можете избавиться от этого предупреждения Es-lint, передав ссылку на него:
Пример указан ниже, однако вы можете посмотреть решение по этой ссылке: https://thewikihow.com/video_r4A46oBIwZk&t=8s
Предупреждение: Строка 13:8: React Hook В React.useEffect отсутствуют зависимости: «история» и «currentUser?.role». Либо включите их, либо удалите массив зависимостей react-hooks/exhaustive-deps
React.useEffect(() => {
if (currentUser?.role !== "Student") {
return history.push("/")
}
}, [])
Разрешение: Шаг 1: Переместите бизнес-логику в отдельный const.
Теперь предупреждение: React Hook React.useEffect имеет недостающую зависимость: «roleChecking».
const roleChecking = () =>{
if (currentUser?.role !== "Student") {
return history.push("/")
}
}
React.useEffect(() => {
roleChecking()
}, [])
Последний шаг — создать ссылку на функцию:
const roleRef = React.useRef();
const roleChecking = () => {
if (currentUser?.role !== "Student") {
return history.push("/");
}
};
roleRef.current = roleChecking;
React.useEffect(() => {
return roleRef.current();
}, [currentUser?.role]);
Кажется, функция fetchBusinesses объявлена в компоненте.
Это означает, что в каждом рендере объявляется новая функция, которая запускает хук.
Есть 2 подхода к решению проблемы.
Переместите объявление функции fetchBusinesses из компонента.
Оберните функцию fetchBusinesses крючком useCallback.
Первый вариант предпочтительнее.
Это не ответ, специфичный для варианта использования вопроса, а более общий случай, и он охватывает случай, когда useEffect или извлечение и импорт не работают. Сценарий useRef:
Иногда сценарий заключается в том, что useEffect должен иметь пустой массив, и вы все еще хотите использовать его внутри частей состояния useEffect, но все же вы не хотите вводить их в качестве зависимостей, также вы можете попробовать использовать useCallback, и теперь реакция жалуется на зависимости использования Callback и вы застряли. При этом в некоторых случаях можно использовать useRef. Например:
const locationRef = useRef(location);
useEffect(()=>{
const qs = locationRef.current.search
...
},[])
Вы должны быть осторожны при использовании этой техники и знать, что useRef не активирует процесс рендеринга.
используя функцию вызова UseEffect fetchBusinesses, объявите в useEffect(), объявив константную переменную после вызова имени функции,
useEffect(()=>{
const fetchBusinesses=()=>{
console.info(useeffect fetchbussinesses functions)
}
fetchBusinesses();
},[declare the variable used in useeffect hooks ])
Поскольку этот вопрос получает много трафика, вот ссылка на Блог Дэна Абрамова, где он подробно объясняет useEffect и его зависимости.