Trying to store FireStore array in React Native? - javascript

I have been trying to push or store a FireStore array in one of my own arrays. I have tried a few versions of code, the first being this:
var data = [];
db.collection('Rooms')
.doc(code)
.get()
.then((docs) => data.push(docs.data()));
However, when I log the data variable, it comes out as an empty array. The second method I have tried is this:
var [data, setData] = useState([]);
db.collection("Rooms")
.doc(code)
.get()
.then((docs) => setData(docs.data()));
However this method seems to setData infinitely, so it is reading into my API infinitely, which I would like to avoid. The last method I tried was this:
var data = db.collection("Rooms").doc(code).get();
console.log(data);
But this just returns
Promise {
"_U": 0,
"_V": 0,
"_W": null,
"_X": null,
}
Could anyone help me with this, ideally I'd like to store the data of an array called "MovieArray" inside the document, but I can't even access the document, so even if you can just help me store the data of the whole document, it would be very helpful.

If you are using react, I would suggest using the hook. You also, don't really need to push objects to an array like that.
Here is an example of how to get some data and store the collection of data.
const Forum = () => {
const [posts, setPosts] = useState(null);
const collectIdsAndDocs = (doc) => {
return { id: doc.id, ...doc.data() };
};
useEffect(() => {
const getPost = async () => {
const snapshot = await firestore.collection('Posts').get();
const myPosts = snapshot.docs.map(collectIdsAndDocs);
console.log(myPosts);
setPosts({ myPosts });
};
const createPost = async (post) => {
const docRef = await firestore.collection('Posts').add(post);
const doc = await docRef.get();
console.log(doc);
};
createPost({ Title: 'My First Post', Content: 'My content' });
getPost();
}, []);
return (
// return some JSX
);
};
Why does this work?
When you get a collection, Firebase returns a snapshot of the collection.
This snapshot has a list of docs or an array if you will.
We then want to map over those docs constructing a new object that contains just the document data and the ID of individual doc. This is what the myPosts variable is.
Using the react state hook, you can set that object to the current state of Posts, in your case this would be rooms.
When you add something to the database, Firestore will return a reference to the newly added item. You can then call get() to get the document back if you need it.

Try changing to (see comment before this)
const [data, setData] = useState({});

Related

Capturing data out of a firebase collection, and actually making it usable?

I have a collection in firebase called "polls". The collection is an array, and within the array I have 4 urls (url_a, url_b, url_c, and url_d). I want to pull these urls out of the array in my react app so I can use them as the src for 4 different images.
I know since "polls" is an array, I can't just pull the urls directly by setting some variable to "polls.data.url_a". With the 2nd useEffect function in the code below, I'm able to console.log the array
and the data within it using forEach. I can console.log poll.data, and even poll.data.url_a and it returns the proper result.
But I am stuck on how to get outside of that forEach and actually capture the individual urls to where I can assign them to a variable so I can make use of them.
import { useEffect, useState } from "react"
import { collection, getDocs } from "firebase/firestore"
import { db } from '../firebase.config'
function Food() {
const [polls, setPolls] = useState([])
useEffect(() => {
getPolls()
}, [])
** useEffect(() => {
polls.forEach((poll) =>
console.log(poll.data.url_a))
}, [polls])
**
function getPolls() {
const pollsRef = collection(db, 'polls');
getDocs(pollsRef).then(response => {
const poll = response.docs.map(doc => ({data: doc.data(), id: doc.id}))
setPolls(poll)
}).catch(error => console.log(error.message))
}
return (
<div>
Food
</div>
)
}
export default Food
I've tried setting a variable within the forEach and calling it later, and running a forEach later on in the function. But I just don't know how to get to the URL in a usable way, outside of just console logging it. Any help would be greatly appreciated, thanks!
Check out what Renaud suggested here to see how to assign variables: Get Firebase Collection into simple array
If I understand what you're asking, you'll need the poll const to instead point towards a global store variable or to reference it within the template section.
If the data your fetching is structured like this:
const polls = [{
data: {
url_a: "url a",
url_b: "url b"
}
}]
It's easier to just assign it to an object state variable instead of an array.
const [polls, setPolls] = useState({})
// ...
useEffect(() => {
getPolls()
}, [polls])
function getPolls() {
const pollsRef = collection(db, 'polls');
getDocs(pollsRef).then(response => {
const polls = response.docs.map(doc => ({data: doc.data(), id: doc.id}))
polls.map(poll => setPolls(poll.data))
}).catch(error => console.log(error.message))
}
// ...
return <img src={polls.url_a} alt="" />

ReactJS call function once if query strings set

Explain:
In useEffect I get products via getProducts() function based on given data and data contains search filters and will be update by user so need to watch it in realtime, for example data contains an object like this {brand: 'x'}
const [data, setData] = useState({});
useEffect(() => {
getProducts(data) // get products via api based on data, if data is clear, it will return all products
.then(data => {
//
});
}, [data]) // watch data in realtime and send it to getProducts()
useEffect(() => {
getQuery(); // check if there are search querys
}, [])
Also there is a getQuery() function, it will check if there are any query strings in search params when page is reload, and will get query strings and set it to data, and by above code if data get update it will call getProducts() again, actually it update products.
const getQuery = () => {
let obj_url = new URL(window.location.href);
let params = obj_url.searchParams;
let searchParams = new URLSearchParams(params);
// some other code
setData(obj); // get query and set it to data
}
Problem:
I noticed while there are query strings in url it will call getProducts() twice! well this code definitely does that, but I don't want it, how can I prevent to call this twice? I just want if there are query strings call once, if not call once.
Yes, it will call it once for the initialization of useState, and second time for the updated data from getQuery on the useEffect. You don't really need a useEffect for the getQuery function. You can just set the data from query on the initial state directly (the initial state will only be set on the initial render anyway, not on subsequent ones).
const [data, setData] = useState(getQuery());
useEffect(() => {
getProducts(data) // get products via api based on data, if data is clear, it will return all products
.then(data => {
//
});
}, [data]) // watch data in realtime and send it to getProducts()
const getQuery = () => {
let obj_url = new URL(window.location.href);
let params = obj_url.searchParams;
let searchParams = new URLSearchParams(params);
// some other code
return obj ?? {}
}
Bad idea to use that function in state initialization, you can do this with native way, react-router-dom. Based on #Cristian-Florin Calina answer, I provide new answer:
import { useRouter } from 'next/router';
const Router = useRouter();
const [data, setData] = useState(Router.query);
useEffect(() => {
getProducts(data)
.then(data => {
//
});
}, [data]);
Router.query return all search params and with this there is no need to call getQuery function. easy!

useState array becomes 2 arrays in console.log?

Im trying to just use a empty array in a react/ts project like this.
const [companyChatrooms, setCompanyChatrooms]: any = useState([]);
I then use a useEffect to get it done when rendering component.
async function fetchMyChatRooms() {
const userCollection = await firestore.collection('user_in_chat')
const snapshot = await userCollection.where('user_id', '==', myIdNumber).where('chatroom_id', '==', companyChatrooms).get();
snapshot.forEach(doc => {
const roomID = doc.data().chatroom_id
setMyChatrooms([...myChatrooms, roomID])
});
}
fetchMyChatRooms()
}, [companyChatrooms, myIdNumber])
console.log(myChatrooms)```
However, my console.log shows 2 arrays with each value instead of 1 array holding both values.
How can i make sure both values are stored in same array?
[1]: https://i.stack.imgur.com/q0WPD.png <-- Check how the output looks.
I assume you have an array snapshot with more than 1 element and any iteration you are updating the state. This caused multiple re-render
I suggest you to update state after iterate entire array. Example:
const rooms = []
snapshot.forEach(doc => {
const roomID = doc.data().chatroom_id;
rooms.push(roomID);
});
setMyChatrooms(rooms)
you should set all of them in one time.
async function fetchMyChatRooms() {
const userCollection = await firestore.collection('user_in_chat')
const snapshot = await userCollection.where('user_id', '==', myIdNumber).where('chatroom_id', '==', companyChatrooms).get();
// here is the changing
const roomIDs = snapshot.map(doc => doc.data().chatroom_id);
setMyChatrooms(roomIDs )
//
fetchMyChatRooms()
}, [companyChatrooms, myIdNumber])
console.log(myChatrooms)

Function not getting all documents in a collection

I know there is a similar question that exists here but it doesn't really answer the question on here. My code currently on get the last two documents in a collection.
const [recipedata, setRecipeData] = useState([]);
const fetchRecipes = async () =>{
const response = fire.firestore().collection("recipes");
const data = await response.get();
data.docs.forEach(item=>{
setRecipeData([...recipedata, item.data()])
})
}
useEffect(() => {
fetchRecipes();
}, [])
Again when I print (recipedata) it only show two objects in the array. Can anyone tell me why this might be happening?
The query is correct and should fetch all the documents in the collection. You should check the the collection again. Also modifying the code like this should be better so you are updating the recipes array only once.
const [recipedata, setRecipeData] = useState([]);
const fetchRecipes = async () =>{
const response = fire.firestore().collection("recipes");
const data = (await response.get()).docs.map(doc => doc.data());
console.log(data)
//Updating only once
setRecipeData(data)
}
useEffect(() => {
fetchRecipes();
}, [])
Please share your collection as well so we can try replicating any issue if it has something to do with Firestore.

fetch json data to state array

I fetch and get the data, I want to save it to state using setState as an array or object so I can call to each field in the return part and display it on screen
but I get this error:
Uncaught Error: Objects are not valid as a React child (found: object
with keys {gender, name, location, email, login, dob, registered,
phone, cell, id, picture, nat}). If you meant to render a collection
of children, use an array instead
import {useState} from 'react'
export default function Test(){
const [data, setData] = useState([]);
const getInfo = async () =>{
const response = await fetch('https://randomuser.me/api');
if(response.status === 200){
const res = await response.json();
setData(res.results[0]);
// also try this: setData(res)
// also try this: setData(res.result)
}else{
throw new Error('Unable to fetch data')
}
}
return(
<div>
<button onClick={() =>{getInfo()}}>fetch data</button>
{data}
</div>
)
}
Please have a look at this codesandbox URL, I feel you can make use of useEffect for your usecase link
The problem is your are getting an response which is not an array. You are trying to use higher order array methode on a object. Use ,
const resultArray = res.results;
get the array of results from the JSON.
It seems like res.results[0] is the object that has the keys of gender, name, location, email, login, dob, registered, phone, cell, id, picture, nat. But you are trying to set the state value data as an array.
You should set the data type to object.
In render function to browse the data, iterate the keys using Object.keys().
for the onClick triggering way in render, you don't need to () => {getInfo()} since there is no specific parameters passed to the method, just use onClick={getInfo}.
import {useState} from 'react'
export default function Test(){
const [data, setData] = useState({});
const getInfo = async () =>{
const response = await fetch('https://randomuser.me/api');
if(response.status === 200){
const res = await response.json();
setData(res.results[0]);
// also try this: setData(res)
// also try this: setData(res.result)
}else{
throw new Error('Unable to fetch data')
}
}
return(
<div>
<button onClick={getInfo}>fetch data</button>
{Object.keys.map(key => (<div>
{key} : {data.key}
</div>)}
</div>
)
}
The data being returned here from "https://randomuser.me/api" is not a simple JSON data. It is really a JSON object with multiple sub-objects in it such as "name", which is again having "title", "first" and "last" as sub-objects. This is the reason you are getting error that "found: object with keys".
If you are looking for dummy data for testing purpose, I would suggest you to check "https://jsonplaceholder.typicode.com/", which is a good source of dummy JSON data.
I have updated your code as under, which is returning data in proper JSON format and can be assigned to state.
const getInfo = async () =>{
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
if(response.status === 200){
const res = await response.json();
console.log(res)
setData(res);
}else{
throw new Error('Unable to fetch data')
}
}
Your problem might be that you are using setData(res.results[0]);
I bet that this is an Object and not an Array.
const [data, setData] = useState([]);
states that the setData function only stores arrays.
try using
const [data, setData] = useState({});
and see if that is working
if it is not working give us the ouput of console.log(res.results)
and console.log(typeof(res.results[0]))

Categories

Resources