Ok so i use the useEffect to get firebase firestore data. This is my hook code:
const [campaign, setCampaigns] = useState([])
useEffect(() => {
var docRef = db.collection("campaigns").doc(slug);
docRef.get().then(function(doc) {
if (doc.exists) {
console.log("Document data:", doc.data());
setCampaigns(doc.data())
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
},[]);
console.log(campaign[0].startDate.seconds) // gives the error stated below
console.log(campaign.images[0].url)// gives the error stated below
The response is:
I can get access to all the top level properties such as body, campaignPolicy etc. but if I do
TypeError: Cannot read property 'seconds' of undefined
If I do campaign.startDate.seconds i get same error:
TypeError: Cannot read property 'seconds' of undefined
Full minimal code:
const ReviewComponent = (props) => {
const [open, setOpen] = useState(false)
const [campaigns, setCampaigns] = useState([])
const [currentCampaign, setCurrentCampaign] = useState([])
//First useEffect hook
const tempDoc = []
useEffect(() => {
var docRef = db.collection("campaigns");
docRef.get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
tempDoc.push({ id: doc.id, ...doc.data() })
// doc.data() is never undefined for query doc snapshots
setCampaigns(tempDoc)
});
})
.catch(function(error) {
console.log("Error getting documents: ", error);
});
},[]);
//Second use effect hook
useEffect(() => {
const docRef = db.collection("campaigns").doc(slug);
docRef.get().then(function(doc) {
if (doc.exists) {
console.log("Document data:", doc.data());
setCurrentCampaign(doc.data())
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
},[]);
const handleOpen = () => {
setOpen(true)
};
const handleClose = () => {
setOpen(false);
};
if(currentCampaign[0]) { // Or if(campaign[0])
console.log(currentCampaign)
}
return(
<Typography>
{currentCampaign.reviews[0].text}
</Typography>
)
You are trying to access your data before they are retrieved. Here is what happens:
// First render
You set your state as an empty array
You declare an effect with a callback that will be called after the first render
You try to access a value that is indeed undefined (your array is empty at this point) and your app crash
// Second render
Your code crash at the first render but if it didn't:
Your state now contains your data
Your effect will not be called as you have an empty dependency array
You can use your data
To make your code work, and you should do it every time you load data asynchronously, is to check that your data are loaded.
const [campaign, setCampaigns] = useState([]);
useEffect(() => {
const docRef = db.collection("campaigns").doc(slug);
docRef.get().then(function(doc) {
if (doc.exists) {
console.log("Document data:", doc.data());
setCampaigns(doc.data())
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
},[]);
if(campaign.length > 0) { // Or if(campaign[0])
console.log(campaign[0].startDate.seconds)
console.log(campaign[0].images[0].url)
}
The default value of campaign is empty array and it is set after few time from firebase store.
So you should check campaign state what is empty or has value.
if (campaign.length == 0)
console.log(campaign[0].startDate.seconds);
Related
I am querying data from firebase, I then want to assign the fetch data to be an array
This is my code
let list = [];
dispatch(fetchUsersPending());
db.collection("users").get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
list.push({...doc.data()});
console.log('All Users: ', list);
dispatch(fetchUsersSuccess(list));
});
}).catch((error) => {
var errorMessage = error.message;
console.log('Error fetching data', errorMessage);
dispatch(fetchUsersFailed({ errorMessage }));
});;
But in my console am getting an error showing Error fetching data Cannot add property 1, object is not extensible in react firebase
I think your current approach also causes to many unnecessary dispatches to the store. With the following solution you only map your array to documents once and then dispatch them all at once.
With async/await
const fetchData = async () => {
try {
dispatch(fetchUsersPending());
const users = (await db.collection('users').get()).docs.map((doc) => ({ ...doc.data()}));
dispatch(fetchUsersSuccess(users));
} catch (errorMessage) {
dispatch(fetchUsersFailed({ errorMessage }));
}
}
With .then()
const fetchData = () => {
dispatch(fetchUsersPending());
const users = db.collection('users').get().then((snapshot) => {
const users = snapshot.docs.map((doc) => ({ ...doc.data() }));
dispatch(fetchUsersSuccess(users));
}).catch((errorMessage) {
dispatch(fetchUsersFailed({ errorMessage }));
});
}
I'm trying to fetch some data with Axios and React, But I'm having a problem resolving the promise and setting it on the state, that's weird.
Here is the Base:
export const fetchUserById = (username) => client.get(`/${username}`);
Here is the Call:
export const getUserById = async (username) => {
try {
const response = await api.fetchUserById(username);
const data = await response.data;
return data;
} catch (error) {
return error;
}
};
Here is in React:
const [user, setUser] = useState();
useEffect(() => {
const data = getUserById(params.username); // this gets the username and its working
setUser(data)
}, [])
useEffect(() => {
console.log("this is user: ", user)
}, [user])
If I console log user, I get undefined, If I console log data i get a promise.
getUserById is declared async so it implicitly returns a Promise that callers should either await or use a Promise chain on.
useEffect(() => {
const data = getUserById(params.username);
setUser(data); // <-- logs only the returned Promise object!
}, [])
async/await
useEffect(() => {
const getUser = async () => {
try {
const data = await getUserById(params.username);
setUser(data);
} catch(error) {
// handle error, log, etc...
}
};
getUser();
}, []);
Promise chain
useEffect(() => {
getUserById(params.username)
.then(data => {
setUser(data);
})
.catch(error => {
// handle error, log, etc...
});
};
}, []);
Or you could as well do:
useEffect(() => {
// fetch data
(async () => {
try {
const data = await getUserById(params.username);
// set state
setUser(data)
} catch(error) {
// handle error, log, etc...
// set init state
setUser(null)
}
})();
}, []);
According to the documentation from firebase you can get a document very simply by using get()
But for some reason in my code it always displays that there's no such document, even though it does exist, this is what I'm doing:
useEffect(() => {
console.log(user, "This is the user UID:"+user.uid)
const userDoc = db.collection('usuarios').doc(user.uid);
const doc = userDoc.get();
if (!doc.exists) {
console.log('No such document!');
}
else {
userDoc
.onSnapshot(snapshot => {
const tempData = [];
snapshot.forEach((doc) => {
const data = doc.data();
tempData.push(data);
});
setUserData(tempData);
})
}
}, [user]);
This is what the console.log() shows:
This is how it looks in firebase:
const doc = userDoc.get();
if (!doc.exists) {
.get returns a promise, so you're checking the .exists property on a promise, which is undefined. You will need to wait for that promise to resolve, either with .then:
userDoc.get().then(doc => {
if (!doc.exists) {
// etc
}
});
Or by putting your code in an async function and awaiting the promise:
const doc = await userDoc.get();
if (!doc.exists) {
// etc
}
If you're using the firebase 8 web version, the userDoc.get() returns a promise, not the document:
userDoc.get().then((doc) => {
if (!doc.exists) {
console.log('No such document!');
} else {
const tempData = [];
const data = doc.data();
tempData.push(data);
setUserData(tempData)
console.log('it worked')
}
}).catch((error) => {
console.log("Error getting document:", error);
});
You can get more info about promises in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises.
In your code you are using the get method to fetch user data and get doesn't provide a snapshot. also, you missed that get() will return a promise so you have to handle using async-await or .then etc.
useEffect(() => {
console.log(user, "This is the user UID:"+user.uid);
getUser(user.uid).then(userData => {
setUserData(userData);
});
}, [user]);
const getUser = async (id) => {
try {
const user = await db.collection('usuarios').doc(id).get();
const userData = user.data();
return userData;
} catch (err){
console.log('Error during get user, No such document!');
return false;
}
Here is how my savedLinksData array prints in my console:
Here is my code that attempts to get the linkType value:
{savedLinksData.map((saved) => {
return <h1>{saved.linkType}</h1>;
})}
What I'm doing wrong?
I think there may be something wrong with the way I stored the values in the array. It doesn't look like the value are inside of the array.
Picture of console.log("----->", saved)
const [savedLinksData, setSavedLinksData] = useState([]);
// query for saved links data
useEffect(() => {
if (user) {
async function fetchData() {
const request = await db
.collection("users")
.doc(user)
.collection("saved")
.onSnapshot((snapshot) =>
setSavedLinks(
snapshot.docs.map((doc) => ({ ...doc.data(), id: doc.id }))
)
);
}
fetchData();
} else {
setSavedLinks([]);
}
}, [user]);
useEffect(() => {
if (savedLinks.length > 0) {
let newArray = [];
savedLinks.map((saved) => {
db.collection("users")
.doc(saved.savedUser)
.collection("links")
.doc(saved.savedLinkId)
.get()
.then(function (doc) {
if (doc.exists) {
// console.log("Document data:", doc.data());
newArray.push(doc.data());
// setSavedLinksData([...savedLinksData, doc.data()]);
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
})
.catch(function (error) {
console.log("Error getting document:", error);
});
});
setSavedLinksData(newArray);
}
}, [savedLinks]);
This is a bit tricky to explain, but here is what I'm doing:
Trying to get json data from an async function called getJsonData() until data is fetched.
After getting the data correctly, I want to get another set of json data from getOtherJsonData()
The following code gets me the first set of data (getJsonData) correctly even after X failures. (if any)
It doens't however get the second set of data (getOtherJsonData) all the time as an error could occur. I want to keep re-execution the bloc of code marked below until the second set of data is returned correctly.
...
import React, {useState, useEffect} from 'react';
import {getJsonData} from './getJsonData';
imoport {getOtherJsonData} from './getOtherJsonData';
const myApp = () => {
const [errorFetchedChecker, setErrorFetchedChecker] = useState(false);
const [isLoading,setIsLoading] = useState(true);
const [data,setData] = useState(null);
const updateState = jsonData => {
setIsloading(false);
setData(jsonData);
};
useEffect(() => {
getJsonData().then(
data => {
updateState(data);
// This is the bloc I want to keep re-executing
//
getOtherJsonData(data.title).then(
otherData => {
updateOtherState(otherData);
console.log("Updated with no error);
},
otherError => {
console.log("Error, try getOtherJsonData again ?");
console.log("Can't try to refresh, no errorFetchedChecker for me :/ ");
}
//
// Until It doesn't return an error
},
error => {
console.log('Error fetching, re-trying to fetch thanks to errorFetchedChecker');
setErrorFetchedChecker(c => !c);
},
);
}, [errorFetchedChecker]);
return (
<View>
<Text>{state.data.title}</Text>
<Text>{data.data.completed}</Text>
</View>
);
}
Here's getJsonData() and getOtherJsonData()
export async function getJsonData() {
try {
let response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
let responseJson = await response.json();
return responseJson;
} catch (error) {
throw error;
// Should I just throw the error here ?
}
}
export async function getOtherJsonData(oldData) {
try {
let response = await fetch(`https://someOtherApilink/${oldData}`);
let responseJson = await response.json();
return responseJson;
} catch (error) {
throw error;
// Should I just throw the error here also ?
}
}
This is my other question which explains how to re-execute the first getJsonData() in case of failure.
Below is something I tried but gave me error about unhandled promises:
const subFunction(myTitle) => {
getOtherJsonData(myTitle).then(
otherData => {
updateOtherState(otherData);
console.log("Updated with no error);
},
otherError => {
console.log("Error, try getOtherJsonData again!");
subFunction(myTitle); //Gives Unhandled promise warning and no result
}
}
useEffect(() => {
getJsonData().then(
data => {
updateState(data);
// This is the bloc I want to keep re-executing
//
subFunction(data.title);
//
// Until It doesn't return an error
},
error => {
console.log('Error fetching, re-trying to fetch thanks to errorFetchedChecker');
setErrorFetchedChecker(c => !c);
},
);
}, [errorFetchedChecker]);
Note: Feel free to rephrase the title in any way, shape or form.
You can try to separate these two functions with using two useEffect, because now you'll have to repeat first request in case of second fail. Something like this:
useEffect(() => {
getJsonData().then(
data => {
updateState(data);
},
error => {
console.log('Error fetching, re-trying to fetch thanks to errorFetchedChecker');
setErrorFetchedChecker(c => !c);
},
);
}, [errorFetchedChecker]);
useEffect(() => {
// prevent request if there's no data
if (data) {
getOtherJsonData(data.title).then(
otherData => {
updateOtherState(otherData);
console.log("Updated with no error);
},
otherError => {
console.log("Error, try getOtherJsonData again ?");
console.log("Can't try to refresh, no errorFetchedChecker for me :/ ");
// you'll have to create one more state for that
setOtherErrorFetchedChecker(c => !c);
}
}
}, [data, otherErrorFetchedChecker])