I'm trying to display a user's order history using query. The users' orders, dubbed "items", is an array and I can see the array being logged in the console with my code below. But nothing is being displayed in the app. What am I missing? Also attached my Firestore db for reference.
const q = query(ordersCollectionRef, where("items", "==", "userId"));
onSnapshot(q, (snapshot) => {
let orders = [];
snapshot.docs.forEach((doc) => {
orders.push({ ...doc.data(), id: doc.id });
});
console.log(orders);
});
...and in the return:
{orders.map((order) => {
if (order.id === currentUser.uid) {
return (
<View>
<Text>test{orders.items}</Text>
</View>
);
};
})}
For React to properly "understand" that you have updated data in a state variable, you need to use the "set state function" and NOT update the state variable directly.
In your code, put the data into a new and temporary variable, the after the forEach() call: setOrders(theTempVariable)
That puts the data in theTempVariable into the orders variable and it tells React that the variable has a new value....so React will "react" to state changing and it will re-render your component.
Related
UPDATE
#Chris's answer below helped me with my issue. Installed the React Firebase Hooks package and set up my code as seen below. Everything is working flawlessly now! :)
const [value] = useCollection(collection(db, `${sessionUserId}`), {
snapshotListenOptions: { includeMetadataChanges: true },
})
useEffect(() => {
if (value) {
const allData = JSON.parse(
JSON.stringify(
value?.docs.map((doc) => ({
id: doc.id as string,
resultData: doc.data() as DocumentData,
}))
)
)
setAllLogs(allData)
}
}, [value])
What am I using in my project? NextAuthjs, Firebase, Recoiljs
I have a useRecoilState hook to save logs that users have created.
const [allLogs, setAllLogs] = useRecoilState(modalAllLogs)
The modalAllLogs looks like this:
export const modalAllLogs = atom<Array<DocumentData>>({
key: 'allLogs',
default: [],
})
Here is the issue. I have a useEffect hook that grabs the data the user has from the database and assigns setAllLogs to that data. Note: I * do not * have the state updating anywhere else.
useEffect(() => {
const docQuery = query(
collection(db, `${sessionUserId}`),
orderBy('timestamp', 'desc')
)
const unsubscribe = onSnapshot(
docQuery,
(snapshot: QuerySnapshot<DocumentData>) => {
const data = JSON.parse(
JSON.stringify(
snapshot.docs.map((doc: QueryDocumentSnapshot<DocumentData>) => ({
id: doc.id as string,
resultData: doc.data() as DocumentData,
}))
)
)
setAllLogs(data)
}
)
return unsubscribe
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [db])
What happens is, on initial render, allLogs returns an empty array, which makes sense, but stays empty until I make any edit within the same file the useEffect hook is in, save it, and NextJS triggers it's fast render, only then does allLogs suddenly display the data properly. I did a console.log(snapshot) and it is empty on every initial render as well.
Whenever I add data using the inputs I have created, Firebase stores all the data properly and correctly. The only issue is when the data is not loaded in on initial render. Other than that, I have no other issues.
I am not sure what I am doing wrong here. Maybe it is the RecoilState that I am utilizing wrong?
I have tried many things from other forums - changing the dependency to allLogs (which gave me an infinite loop), tried using getDocs() instead of onSnapshot(), and etc.
The problem here is, that the useEffect isn't triggered because the "db" constant doesn't change.
Have a look at these custom hooks for Firebase/firestore https://github.com/CSFrequency/react-firebase-hooks/tree/master/firestore
or lift the onSnapshot up to get real-time results. But in the last case the renders won't be triggered so I think it's the best to make use of custom hooks.
I have been stuck at this for a while. I'm using redux to maintain the data in my react app and using firebase database to store it. I'm unable to assign the name property of firebase as an id to their respective item.
I get this error message - Warning: Each child in a list should have a unique "key" prop.
This is how I'm assigning the id's.
This is the action that fetches data from firebase.
export const FetchPost = () => {
return dispatch => {
dispatch(FetchPostStart());
axios.get('/Data.json')
.then(response => {
const fetchedData = [];
for(let key in response.data){
fetchedData.push({
...response.data[key],
id: response.data[key].name
});
}
dispatch(FetchPostSuccess(fetchedData));
})
.catch(error => {
dispatch(FetchPostError(error));
});
}
}
This is when I render the data from redux store to the screen.
{this.props.data.reverse().map((res) => (
<div>
<Card
key={res.id}
className="Cards"
>
But the id assigned is null to all the items.
I am trying to render my firestore data to my web app using React.
here is my code
I can see the document ID when I console.log(posts.id), however I cannot find the content of that document.
Here are the content of a document
The method for getting the data is .data, as in:
console.log(posts[0].data())
To render something for each post might be something like:
<p>
{posts.map(doc => {
const data = doc.data();
return (
<div>{data.title}</div>
)
})}
</p>
Personally, unless i need something other than the data from the document, i tend to do my calls to .data() before i even put it into react state. That way i don't need to do it on each render. e.g:
useEffect(() => {
database.posts.get().then(snapshot => {
setPosts(snapshot.docs.map(doc => doc.data());
});
});
I use React with Redux and Firebase. Here is one of the functions from my Action.js
export const loadItemsInCategory = (categoryId) => {
return (dispatch) => {
let itemsArray = [];
firestoreService.getItemsInCategory(categoryId)
.then(updatedGroceryList => {
itemsArray = updatedGroceryList;
console.log(`category id is ${categoryId}`)
dispatch(loadItemsInCategoryHelper(categoryId, itemsArray))
})
.catch((error) => console.log(error));
}
}
It's a normal FireStore query. Here is what happens in firestoreService.getItemsInCategory(categoryId)
export const getItemsInCategory = async (categoryId) => {
console.log(`firebase category id is ${categoryId}`)
const snapshot = await db.collection('Item').where('category', '==', categoryId).get()
return snapshot.docs.map(doc => {console.log("called");return {id: doc.id, ...doc.data()}});
}
Right now, my application shows the list of items in the given Category. However, the list does not get updated when a new Item is added to the category by someone else. In other words, additions in FireStore collection does not reflect on my screen unless I refresh the page.
How can I code my webapp in such a way that any change on the FireStore end gets reflected on my webapp?
Thanks in advance!
Your code is doing a one-time query with get(). Queries made like this are not realtime. They don't refresh.
If you want to receive updates to your query in realtime, you should follow the documentation for realtime queries. Instead of using get(), you will use onSnapshot(). And instead of getting a promise, you will attach a listener callback that will be invoked whenever there is a change in the results of the query. Because of these differences, your code will look drastically different.
I'm trying to retrieve data (let's say a list of todos) from Firestore and render it to the DOM, but I'm unsure how to properly do this/what the best practice is. Do I retrieve the data from Firestore using useEffect and onSnapshot and store it in state? When adding, deleting, and/or changing data (todos), is it as simple as just using the .add(), .delete(), .update() Firestore methods to update the database, and thus updating the DOM (because of the onSnaphot)?
The code below works, but I'm interested in learning the best way to go about this problem. I heard storing data in state isn't a very good idea so that is another reason I'm asking.
const [ todos, setTodos ] = useState([])
useEffect( () => {
const db = firebase.firestore().collection('todos')
db.onSnapshot( snapshot => {
const retrievedTodos = []
snapshot.forEach( doc => {
retrievedTodos.push({...doc.data(), id: doc.id})
})
setTodos(retrievedTodos)
})
}, [])
return (
<div>
{todos.map( todo => {
return <TodoItem
text={todo.text}
key={todo.id} />
})}
</div>
)