Не получает данные из firestore

#javascript #reactjs #firebase #google-cloud-firestore

#javascript #reactjs #firebase #google-облако-firestore

Вопрос:

Я попытался извлечь документы из firestore, он возвращает пустой массив, но когда я запускаю console.log(docs); вне объявленной функции он возвращает фактический массив. Я знаю, что эта ошибка возникает из-за того, что моя функция useEffect запускается первой перед получением документов из firestore. Я хочу знать, как исправить эту проблему.

 const Component = (props) => {
    const { docs } = useFirestore('books');
    const id = props.match.params.id;
    
    const loadContent = () => {
        const book = docs amp;amp; docs.filter(doc => doc.id === id);
        console.log(book); //not getting book from the docs because docs is empty
    }
    
    useEffect(() => {
        async function getContent(){
            await loadContent();
        }
        getContent()
    },[]);
};
  

useFirestore.js

 import { useState, useEffect } from 'react';
import { firestore } from '../config/fbConfig';

const useFirestore = (collection) => {
    const [docs, setDocs] = useState([]);
    const [loading, setLoading] = useState(true);

    // getting realtime data from the firebase for books
    useEffect(() => {
        let unsubscribe = firestore.collection(collection)
        // .orderBy('createdAt', 'desc')
        .onSnapshot((querySnapshot) => {
          const items = [];
          querySnapshot.forEach((doc) => {
            items.push({...doc.data(), id: doc.id});
          });
          setDocs(items);
          setLoading(false);
        });
        return unsubscribe;
      }, []); // eslint-disable-line react-hooks/exhaustive-deps
  
    return { docs, loading };
}

export default useFirestore;
  

Комментарии:

1. Является ли useFirestore пользовательским кодом или из какой-либо библиотеки?

2. расширяя комментарий @farvilain — это совсем не типичный код Firestore — кроме слова «useFirestore» (которое, я полагаю , является каким-то пользовательским хуком?), Ни один из остальных не похож на какой-либо типичный код Firestore.

3. useFirestore — это пользовательский хук, который принимает collection в качестве аргумента, а затем получает данные и возвращает документ.

Ответ №1:

Я думаю, вы довольно близки к получению ожидаемого поведения. Вот как я бы подошел к этому:

 const Component = props => {
  const { docs } = useFirestore("books");

  const id = props.match.params.id;

  // useMemo - whenever `docs` change, recalculate the book variable.
  // If `docs` don't change, `book` will also not change.
  // Your `docs` will probably change on every snapshot.
  const book = useMemo(() => docs amp;amp; docs.filter(doc => doc.id === id), [docs]);

  console.log(book);

  useEffect(() => {
    if (book) {
      // Do something with the book, e.g. loadContent(book).
      // Keep in mind that this will run on every snapshot.
      // If you only want to run this once, you'll need an
      // extra state variable to store that the effect was
      // already run, and check it here.
    }
  }, [book]); // The effect will run whenever book changes
};
  

useFirestore Перехват выглядит почти нормально, только одно замечание: прямо сейчас, даже если вы измените collection параметр, прослушиватель моментальных снимков не изменится. Возможно, вы захотите сделать это:

 useEffect(() => {
  const unsubscribe = firestore.collection(collection).onSnapshot(snapshot => {
    const items = snapshot.map(doc => ({ ...doc.data(), id: doc.id }));
    setDocs(items);
    setLoading(false);
  });

  return unsubscribe;

  // Whenever `collection` changes, `unsubscribe` will be called, and then this hook
  // will subscribe to the new collection.
}, [collection]);
  

Upd.

Если вы хотите useFirestore , чтобы перехват запрашивал только определенную книгу, вам нужно изменить перехват, чтобы он принимал и использовал идентификатор документа, что-то вроде этого:

 
const getDoc = doc => ({ ...doc.data(), id: doc.id });

const useFirestore = (collection, docId) => {
  const [docs, setDocs] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let subject = firestore.collection(collection);

    if (docId) {
      // If docId is available, listen for changes to a
      // particular document
      subject = subject.doc(docId);
    }

    const unsubscribe = subject.onSnapshot(snapshot => {
      // Notice here that if listening for a particular docId,
      // the snapshot will be that document, not an array.
      // To maintain the interface of the hook, I convert that
      // document to an array with a single item.
      const items = docId ? [getDoc(doc)] : snapshot.map(getDoc);
      setDocs(items);
      setLoading(false);
    });

    return unsubscribe;
  }, [collection, docId]);

  return { docs, loading };
};
  

Комментарии:

1. Привет, Avius, я хочу знать, вызываю ли я хук useFirestore и хочу только 1 конкретную книгу (например, в частности, сведения о книге и компонент edit book details), но хук useFirestore возвращает все документы books, это будет стоить мне больше чтения. Как мне этого избежать?

2. Вам нужно прослушать снимки определенного документа. См. Обновление.

Ответ №2:

Мне нужно больше информации о коде ‘useFirestore’, но вы должны хотя бы написать свой код таким образом.

  • не перечисляйте каждый документ в firestore, чтобы получить только один (вы платите за каждый запрос на чтение)

  • загрузите документ в useEffect, а не снаружи

  • Эффект использования должен зависеть от идентификатора

        const Component = (props) => {
           const id = props.match.params.id;
           const firestore = //;
           const [book, bookSet] = useState(false);
    
           useEffect(() => {
               //Depending on useFirestore code
               firestore.collections('books').doc(id)
               .then( snapshot => {
                   if ( !snapshot.exists ) {
                       bookSet(null);
                   } else {
                       bookSet(snapshot.data());
                   }
               });
           }, [id]);
    
           if( book === false) return <p>Loading</p>
           if (!book) return <p>Not exists</p>
           return <p>Display it</p>;
       };
      

Издание

Вот мое предположение о ваших крючках ‘useFirestore’

   const Component = (props) => {
      const id = props.match.params.id;
      const { docs, loading } = useFirestore('books');
      
      useEffect(() => {
          if( loading) console.log('Loading');
          return;

          const book = docs amp;amp; docs.filter(doc => doc.id === id);
          console.log({book});
      },[loading, docs, id]);
  };
  

Комментарии:

1. Привет, farvilain, спасибо за ваш ответ, я добавил код useFirestore. пожалуйста, посмотрите и предложите мне какой-нибудь способ получить массив документов. Моя цель — отфильтровать конкретную книгу, а затем поместить сведения о книге в форму редактирования, а затем обновить сведения о книге. Проблема заключается в получении этих конкретных данных книги. Пожалуйста, помогите мне с этой проблемой.

2. Я не эксперт по реакции, но я думаю, что это не способ написать хук. Поэтому я обновлю свой пост, чтобы отразить ваш useFirestore… но опять же, это кажется мне неправильным. Возможно, вам следует рассмотреть возможность вызова Firestore непосредственно внутри компонента или использовать некоторые существующие библиотеки react-firestore

3. Нет, я не могу заставить это работать, я предоставлю вам другой метод, но не смогу использовать этот useFirestore. Не могли бы вы согласиться прекратить его использование?