Как исправить отсутствующую зависимость в React Hook useEffect
С 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/issues/14920 где возможное решение You can always // eslint-disable-next-line react-hooks/exhaustive-deps if you think you know what you're doing.
Я не уверен в том, что я делаю, поэтому я еще не пытался реализовать это.
У меня есть эта текущая настройка React ловушка 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
});
};
27 ответов
Если вы не используете метод 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 за пределами рендера, вы должны отметить две вещи
- Если есть какие-либо проблемы, если вы не проходите
fetchBusinesses
как метод и то, что он используется во время монтирования с закрывающим замком, проблем не будет. - Зависит ли ваш метод от каких-то переменных, которые он получает из закрытого замыкания, что не так для вас.
- При каждом рендеринге fetchBususiness будет воссоздан, и, следовательно, передача его в useEffect вызовет проблемы. Итак, сначала вы должны запомнить fetchBususiness, если вы должны были передать его в массив зависимостей.
Подводя итог, я бы сказал, что если вы используете fetchBusinesses
вне useEffect
Вы можете отключить правило, используя // eslint-disable-next-line react-hooks/exhaustive-deps
в противном случае вы можете переместить метод внутри useEffect.
Вы можете установить его прямо как 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
./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, а предупреждение Elsint.
Это говорит вам, что крючок зависит от функции fetchBusinesses
, так что вы должны передать это как зависимость.
useEffect(() => {
fetchBusinesses();
}, [fetchBusinesses]);
Это ничего не изменит, если fetchBusinessess
вне компонента в работе, но предупреждение исчезнет.
Эти предупреждения очень полезны для поиска компонентов, которые не обновляются последовательно: 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
}
}
Решение также дает реакция, они советуют вам использовать useCallback
который вернет мемоизируемую версию вашей функции:
Функция 'fetchBus Business' изменяет зависимости хука useEffect (в строке NN) при каждом рендеринге. Чтобы исправить это, оберните определение fetchBuscies в его собственное useCallback() Hook response-hooks / excustive-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]);
const [mount, setMount] = useState(false)
const fetchBusinesses = () => {
//function defination
}
useEffect(() => {
if(!mount) {
setMount(true);
fetchBusinesses();
}
},[fetchBusinesses]);
Это довольно простое решение, и вам не нужно переопределять предупреждения es-lint. Просто установите флаг, чтобы проверить, смонтирован ли компонент или нет.
Просто отключите eslint для следующей строки;
useEffect(() => {
fetchBusinesses();
// eslint-disable-next-line
}, []);
таким образом вы используете его так же, как компонент примонтировал (вызывается один раз)
обновлено
или
const fetchBusinesses = useCallback(() => {
// your logic in here
}, [someDeps])
useEffect(() => {
fetchBusinesses();
// no need to skip eslint warning
}, [fetchBusinesses]);
fetchBusshops будет вызываться каждый раз, когда что-то изменится
Эта статья является хорошим учебником по извлечению данных с помощью хуков: https://www.robinwieruch.de/react-hooks-fetch-data/
По сути, включить определение функции выборки внутри useEffect
:
useEffect(() => {
const fetchBusinesses = () => {
return fetch("theUrl"...
// ...your fetch implementation
);
}
fetchBusinesses();
}, []);
На самом деле предупреждения очень полезны при разработке с хуками. но в некоторых случаях он может вас уколоть. особенно когда вам не нужно прислушиваться к изменению зависимостей.
Если вы не хотите ставить fetchBusinesses
внутри зависимостей ловушки вы можете просто передать ее в качестве аргумента функции обратного вызова ловушки и установить основной fetchBusinesses
в качестве значения по умолчанию для него вот так
useEffect((fetchBusinesses = fetchBusinesses) => {
fetchBusinesses();
}, []);
Это не лучшая практика, но в некоторых случаях может быть полезна.
Также, как написал Шубнам, вы можете добавить нижеприведенный код, чтобы ESLint игнорировал проверку вашего хука.
// eslint-disable-next-line react-hooks/exhaustive-deps
Ты попробуй этот путь
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
Это не ответ для конкретного варианта использования вопроса, а более общий случай, и он охватывает случай, когда useEffect или extract and import не работают. Параметр useRef senario:
Иногда сценарий таков, что useEffect должен иметь пустой массив, и вы все еще хотите использовать внутри useEffect части состояния, но все же вы не хотите вводить их как зависимости, также вы можете попробовать useCallback, и теперь реагируете, жалуется на зависимости использования useCallback, и вы застряли. В этом случае в некоторых случаях можно использовать useRef. Например:
const locationRef = useRef(location);
useEffect(()=>{
const qs = locationRef.current.search
...
},[])
Вы должны быть осторожны при использовании этой техники и знать, что useRef не активирует процесс рендеринга.
Well if you want to look into this differently, you just need to know what are options does the React has that non
exhaustive-deps
? One of the reason you should not use a closure function inside the effect is on every render, it will be re-created/destroy again.
So there are multiple React methods in hooks that is considered stable and non-exhausted where you do not have to apply to the
useEffect
dependencies, and in turn will not break the rules engagement of
react-hooks/exhaustive-deps
. For example the second return variable of
useReducer
or
useState
which is a function.
const [,dispatch] = useReducer(reducer, {});
useEffect(() => {
dispatch(); // non-exhausted, eslint won't nag about this
}, []);
So in turn you can have all your external dependencies together with your current dependencies coexist together within your reducer function.
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]);
В моем случае это предупреждение было с моей локальной переменной, и когда я поставил
organization
в массиве зависимостей будет извлекаться бесконечно. Поэтому, если у вас есть некоторые пробелы, подобные моему, используйте с массивом зависимостей и разделите:
потому что, если у вас есть несколько вызовов 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])
Вы можете удалить массив типа 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]);
// ...
}
Просто передайте функцию в качестве аргумента в массиве useEffect...
useEffect(() => {
functionName()
}, [functionName])
Если вы хотите отключить это бесполезное сообщение, просто добавьте его в начало вашего файла.
/* eslint-disable react-hooks/exhaustive-deps */
Это отключит предупреждение для всего файла.
Найдите ключевые слова, чтобы узнать больше о каждом предупреждении. Чтобы игнорировать, добавьте // eslint-disable-next-line к предыдущей строке.
Например: функция, используемая в useEffect, вызывает предупреждение.
Вы можете избавиться от этого предупреждения Es-lint, передав ссылку на него:
Пример, упомянутый ниже, однако вы можете посмотреть решение по этой ссылке: https://www.youtube.com/watch?v=r4A46oBIwZk&t=8s
Предупреждение: Строка 13: 8: React Hook React.useEffect имеет недостающие зависимости: 'history' и 'currentUser? .Role'. Либо включите их, либо удалите массив зависимостей react-hooks / excustive-deps
React.useEffect(() => {
if (currentUser?.role !== "Student") {
return history.push("/")
}
}, [])
Решение. Шаг 1. Переместите бизнес-логику в отдельную константу.
Теперь предупреждение: 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()
а затем использовали его внутри, вам не нужно было бы добавлять его в массив зависимостей. Вот как мы на самом деле настраиваем наши приложения redux: мы всегда импортируем наших создателей действий и запускаем их внутри
useEffect
без добавления его в массив зависимостей.
То же верно и для
useMemo
тоже.
It seems the function declared in the component.It means in every render it declares new function which triggers the hook.
There are 2 approaches to fix the issue.
Move the function declaration out of component.
Wrap the
fetchBusinesses
function withuseCallback
hook.
First option is preferable.
используя UseEffect
fetchBusinesses
вызывающая функция объявляет в
useEffect()
объявив константную переменную после вызова имени функции,
useEffect(()=>{
const fetchBusinesses=()=>{
console.log(useeffect fetchbussinesses functions)
}
fetchBusinesses();
},[declare the variable used in useeffect hooks ])
Чтобы отключить это предупреждение в вашем проекте
Добавь это"react-hooks/exhaustive-deps": "off"
к.eslintrc.js
файл
Я хочу только бежать [
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
снаружи работает здесь хорошо, так как в любом случае мы сможем читать только начальные свойства и состояние из компонента из-за устаревшей области закрытия ([]
Деп в useEffect
).
Как опустить зависимости функций
- Функция перемещения внутри эффекта
- Переместить функцию за пределы компонента - (мы используем эту)
- Вызвать функцию во время рендеринга и позволить
useEffect
зависят от этого значения (чистая вычислительная функция) - Добавьте функцию для эффекта deps и оберните ее
useCallback
в крайнем случае
По поводу других решений:
Тянуть fetchBusinesses
внутри useEffect()
на самом деле не помогает, если вы получаете доступ к другому состоянию в нем. eslint все равно будет жаловаться: Codesandbox.
Я бы также воздержался от исчерпывающих комментариев eslint-deps ignore comments. О них просто легко забыть, когда вы проводите некоторый рефакторинг и пересмотр своих зависимостей.
Попробуйте поместить функцию в useEffect. В моем случае это помогло. Удачного кодирования !!
Вы используете, и когда вы это делаете, очень часто вы хотите использовать некоторые переменные, которые используются в качестве реквизита или состояния внутри вашего компонента.
В eslint встроено правило, которое требует, чтобы вы ссылались на любое другое свойство или часть состояния внутри массива зависимостей. Это массив, который контролирует, когда выполняется. Это правило хочет видеть его в списке внутри этого массива, который решает, когда повторно запускать функцию.
Таким образом, вам нужно будет добавить в
[fetchBusinesses]
и предупреждение должно исчезнуть.
Теперь, почему это правило хочет, чтобы мы поместили это туда?
Есть несколько сценариев, в которых использование и неправильное перечисление всех частей состояния и свойств внутри массива может привести к странным и трудным для отладки проблемам.
Таким образом, это правило должно помочь избежать трудных для понимания проблем, которые могут возникнуть.
Теперь произвольное добавление к этому массиву также может привести к ошибкам. Так что в любом случае вы сталкиваетесь с ошибками, которые вы должны решить. Согласно вашим комментариям, которые, казалось, решили это для вас, но я хотел бы продолжить расследование, чтобы узнать, есть ли у вас случайно второй
GET
запрос на вкладке « Сеть » в Chrome после добавления в
fetchBusinesses
функция для вашего
useEffect
множество.
Добавьте этот комментарий вверху файла, чтобы отключить предупреждение.
/* eslint-disable react-hooks/exhaustive-deps */