Я создаю несколько хуков API, и у меня проблемы со свойством deps
. Я хочу повторно вызывать useEffect
при изменении содержимого deps
, вместо этого я работаю в вечном цикле. deps
может быть неопределенным или any[]
export const useReader = <TResponseData>({ url, deps, options }: TypeUseReader<TResponseData>) => {
const [data, setData] = useState<TResponseData | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [error, setError] = useState<number | null >(null);
async function fetchData(newUrl?: string) {}
useEffect(() => {
// ..fetch
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [url, deps]); // HERE, deps are forever loop
return {
data,
isLoading,
error,
fetchData
};
};
Вот как я его использую:
return useReader<FOO>(
{
url: 'URL_PLACEHOLDER',
deps: [anything] /* FIXME: deps enters FOREVER LOOP */
}
);
Что я пробовал:
[deps ?? []]
и ...[deps ?? []]
и deps
- все это создает вечный цикл
@NirAlfasi Я переключился на использование deps
напрямую, чтобы удалить options
опору из уравнения, и обновил код. Но я все еще получаю вечный цикл
Проблема в том, что когда вы вызываете useReader
с deps
в качестве литерала массива, каждый раз создается новый массив, поэтому useEffect
правильно определяет изменение значения.
Вообще говоря, есть два способа справиться с этим.
Вы можете сделать это ответственным за вызывающую сторону — укажите в useReader
интерфейсе, что она принимает одно dep
значение типа unknown
. Если вызывающая сторона хочет пройти через массив значений, они должны быть стабилизированы, например.
const dep = useMemo(() => [val1, val2], [val1, val2]);
return useReader<FOO>(
{
url: 'URL_PLACEHOLDER',
isSuspended: false,
initialValue: foo,
dep /* only changes if val1 or val2 has changed */
}
);
Проще и гибче принять массив deps
(напечатанный как unknown[]
) и просто распределить элементы массива после других зависимостей, которые вы передаете useEffect
:
useEffect(() => {
if (!isSuspended) {
fetchData();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isSuspended, url, ...(deps ?? [])]);
Различные линтеры расстраиваются из-за этого, но это не проблема в том, что касается механизма перехвата.
Спасибо. Это работает! Теперь я ясно понимаю, почему у него был вечный цикл, я каждый раз создавал новый массив. Но почему ...(deps ?? [])
тоже не вызывает бесконечный цикл? Деструктуризация возвращает новое значение. ИЛИ это потому, что деструктуризация не изменяет ссылки?
Вы не поделились определением TypeUseReader<TResponseData>
, поэтому я не уверен, считаете ли вы deps
необязательным. Если deps
не определено, то deps ?? []
просто вернется к пустому массиву. Распространение пустого массива не влияет на содержимое зависимостей useEffect
, и только содержимое определяет, будет ли эффект повторно запущен. Если вам требуется deps
в качестве параметра для useReader
, вы можете просто использовать ...deps
вместо ...(deps ?? [])
deps
необязательно, но если я правильно понял ваше объяснение. Это не проблема, потому что распространение пустого массива не влияет на массив зависимостей useEffect
.
Ваше понимание правильное (или, по крайней мере, мое понимание вашего понимания :-)
Вы пытались заменить
[options?.deps ?? []]
как зависимость вuseEffect
на:options
? Я подозреваю, что обертывание всего этого массивом заставляет его переоценивать новый объект каждый раз, когда он проверяется.