ReactJS и квота Firebase достигается очень быстро с небольшими данными - PullRequest
1 голос
/ 04 августа 2020

Может ли кто-нибудь помочь с объяснением, как я смогу достичь своих 50 000 ежедневных чтений с небольшим объемом хранимых данных? Моя база данных состоит из категорий и элементов, и у меня было около 4 категорий и, возможно, 5 элементов, которые были сохранены, и менее чем за несколько часов она достигла 50 КБ чтения.

У меня есть такие настраиваемые хуки:

export const useItems = (selectedCategory) => {
  const [items, setItems] = useState([]);
  const [archivedItems, setArchivedItems] = useState([]);

  useEffect(() => {
    let unsubscribe = firebase
      .firestore()
      .collection("items")
      .where("userId", "==", "1");

    unsubscribe =
      selectedCategory && !collatedItemsExist(selectedCategory)
        ? (unsubscribe = unsubscribe.where(
            "categoryId",
            "==",
            selectedCategory
          ))
        : selectedCategory === "INBOX" || selectedCategory === 0
        ? (unsubscribe = unsubscribe.where("date", "==", ""))
        : unsubscribe;

    unsubscribe = unsubscribe.onSnapshot((snapshot) => {
      const newItems = snapshot.docs.map((item) => ({
        id: item.id,
        ...item.data(),
      }));

      setItems(newItems.filter((item) => item.archived !== true));
      setArchivedItems(newItems.filter((item) => item.archived !== false));
    });

    return () => unsubscribe();
  }, [selectedCategory]);
  return { items, archivedItems };
};

export const useCategories = () => {
  const [categories, setCategories] = useState([]);

  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]);

  return { categories, setCategories };
};

Затем на моей странице Items.jsx у меня есть это:

export const Items = () => {
  const { selectedCategory } = useSelectedCategoryValue();
  const { categories } = useCategoriesValue();
  const { items } = useItems(selectedCategory);

  let categoryName = "";

  if (categories && selectedCategory && !collatedItemsExist(selectedCategory)) {
    categoryName = getTitle(categories, selectedCategory).name;
  }

  if (collatedItemsExist(selectedCategory) && selectedCategory) {
    categoryName = getCollatedTitle(collatedItems, selectedCategory).name;
  }

  useEffect(() => {
    document.title = `${categoryName}`;
  });

  return (
    <div className="tasks" data-testid="tasks">
      <h2 data-testid="project-name">{categoryName}</h2>

      <ul className="tasks__list">
        {items.map((item, i) => (
          <li key={`${item}-${i}`}>
            <div className="flex tasks-list-price-item">
              <span>{item.item}</span>
              <span>{item.price}</span>
            </div>
            <div>
              <span>{item.description}</span>
            </div>
          </li>
        ))}
      </ul>
      <AddItem />
    </div>
  );
};

Это потому, что useEffect запускается несколько раз, непрерывно считывая все данные? Есть ли способ оптимизировать чтение, чтобы одни и те же данные не читались снова и снова? Какие из лучших практик мне следует изучить?

Следует ли мне использовать что-то еще, например mongodb вместо firebase?

1 Ответ

3 голосов
/ 04 августа 2020

Зависимости 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, вы можете сделать это, установив правильную зависимость в массиве зависимостей.

...