Зависимости useEffect
Очень важно понимать второй аргумент useEffect
. Вот цитата из официальной документации: https://reactjs.org/docs/hooks-reference.html#conditionally -firing-an-effect
По умолчанию эффекты запускаются после каждого завершенного рендеринга. Таким образом, эффект всегда воссоздается при изменении одной из его зависимостей.
Однако в некоторых случаях это может быть излишним, как в примере подписки из предыдущего раздела. Нам не нужно создавать новую подписку при каждом обновлении, только если свойство источника изменилось.
Чтобы реализовать это, передайте второй аргумент useEffect, который представляет собой массив значений, от которых зависит эффект. Наш обновленный пример теперь выглядит следующим образом:
useEffect(
() => {
const subscription = props.source.subscribe();
return () => {
subscription.unsubscribe();
};
},
[props.source],
);
Эффект будет активирован один раз после первого рендеринга, и после каждого рендеринга, в котором изменяется зависимость (props.source
). .
Текущий анализ случая
useEffect(() => {
firebase
.firestore()
.collection("categories")
.where("userId", "==", "1")
.orderBy("categoryId")
.get()
.then((snapshot) => {
const allCategories = snapshot.docs.map((category) => ({
...category.data(),
docId: category.id,
}));
if (JSON.stringify(allCategories) !== JSON.stringify(categories)) {
setCategories(allCategories);
}
});
}, [categories]);
Что здесь происходит?
- После первого рендеринга эффект срабатывает, и начинается загрузка категорий из firebase.
- После получения категорий он сравнивает загруженные категории с текущим пустым состоянием, так что условие истинно, и сохраняет загруженные категории в этом состоянии.
- Состояние (переменная
categories
) был обновлен (обновление состояния в React вызывает новый render
), запускается новый рендеринг с обновленным categories
состоянием - Поскольку состояние
categories
является зависимостью от эффекта, эффект запускается после рендеринга, он снова загружает категории из firebase. - Оператор if проверяет, совпадают ли категории из состояния с недавно загруженной cate кровь. Если данные отличаются, он сохраняется в этом состоянии и снова активирует эффект после повторной визуализации. Если данные совпадают, здесь останавливаются.
Заключение
Как вы указали в комментариях, проблема заключалась в том, что ваше условие никогда не было ложным, поэтому оно получало категории поверх и снова, пока не будет достигнута квота.
Вероятно, вы захотите получить категории только один раз, вам не нужно загружать их даже два раза. Итак, лучшее решение - удалить зависимость categories
из массива зависимостей. Также, если вы выбираете категории только один раз, вам не нужно условие, сравнивающее текущее состояние с загруженными категориями.
Поэтому я бы посоветовал изменить код на этот:
useEffect(() => {
firebase
.firestore()
.collection("categories")
.where("userId", "==", "1")
.orderBy("categoryId")
.get()
.then((snapshot) => {
const allCategories = snapshot.docs.map((category) => ({
...category.data(),
docId: category.id,
}));
setCategories(allCategories);
});
}, []);
Если вам по какой-то причине необходимо повторно получить категории из api, вы можете сделать это, установив правильную зависимость в массиве зависимостей.