我试图从Firestore中获取文档,但它返回一个空数组,但是当我运行console.log(docs);时,它会返回一个空数组。在声明的函数之外,它返回实际的数组。我知道会发生此错误,因为在从Firestore获取文档之前,我的useEffect函数首先运行。 我想知道如何解决此问题。
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()
},[]);
};
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;
答案 0 :(得分:1)
我需要有关“ 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]);
};
答案 1 :(得分: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 };
};