【问题标题】:Not getting data from the firestore未从 Firestore 获取数据
【发布时间】:2020-10-30 14:56:54
【问题描述】:

我试图从 firestore 获取文档,它返回一个空数组,但是当我运行 console.log(docs);在声明的函数之外,它返回实际的数组。我知道发生此错误是因为我的 useEffect 函数在从 firestore 获取文档之前首先运行。 我想知道如何解决这个问题。

const Component = (props) => {
    const { docs } = useFirestore('books');
    const id = props.match.params.id;
    
    const loadContent = () => {
        const book = docs && 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()
    },[]);
};

使用Firestore.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;

【问题讨论】:

  • useFirestore 是自定义代码还是来自任何库?
  • 扩展 @farvilain 的评论 - 这根本不是典型的 Firestore 代码 - 除了“useFirestore”这个词(我是某种自定义钩子? ),其余的都不像任何典型的 Firestore 代码。
  • useFirestore 是一个自定义钩子,它将集合作为参数,然后获取数据并返回文档。

标签: javascript reactjs firebase google-cloud-firestore


【解决方案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 && 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]);

更新。

如果您希望 useFirestore 挂钩仅查询特定书籍,则需要更改挂钩以接受并使用文档 ID,如下所示:


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 };
};

【讨论】:

  • 您好 Avius,我想知道如果我调用 useFirestore 钩子并且只想要 1 本书(例如特定书籍详细信息和编辑书籍详细信息组件)但 useFirestore 钩子返回所有书籍文档,这将花费我读多了。我应该如何避免这种情况?
  • 您需要监听特定文档的快照。查看更新。
【解决方案2】:

我需要有关“useFirestore”代码的更多信息,但您至少应该这样编写代码。

  • 不要列出 firestore 上的每个文档来只获取一个(您为每个读取请求付费)

  • 在 useEffect 中加载文档,而不是在外部

  • useEffect 必须依赖于 id

       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 && docs.filter(doc => doc.id === id);
          console.log({book});
      },[loading, docs, id]);
  };

【讨论】:

  • 您好 farvilain,感谢您的回复,我添加了一个 useFirestore 代码。请看一下并向我建议一些获取文档数组的方法。我的目的是过滤特定书籍,然后将书籍详细信息放入编辑表单中,然后更新书籍详细信息。问题是获取特定的书籍数据。请帮我解决这个问题。
  • 我不是反应专家,但我认为这不是编写钩子的方法。所以我会更新我的帖子以反映你的 useFirestore ......但在我看来,这似乎是错误的。也许您应该考虑直接在组件内部调用 Firestore 或使用一些现有的 react-firestore 库
  • 不,我不能让它工作,我会为你提供另一种方法,但不能使用这个 useFirestore。你能接受停止使用它吗?
猜你喜欢
  • 1970-01-01
  • 2021-09-29
  • 1970-01-01
  • 1970-01-01
  • 2018-11-06
  • 2021-09-04
  • 1970-01-01
相关资源
最近更新 更多