I have a list of data from my firebase firestore that I want to export to .csv
I did everything that is required but when I add the values that I want to be exported they are always undefined.
I am not an expert in react I am somewhat intermediate but I think it is because I am setting my data inside a useEffect Hook.
My data useState
is undefined, although it holds values and I can see them in my table, which is causing the CSVLink to throw errors.
How do I allow my data to be passed into the headers?
Here is my code:
const [data, setData] = useState([]);
const [id, setID] = useState("");
const list = []
const filteredList = []
useEffect(() => {
firebase.firestore().collection("Users").get().then((userSnapshot) => {
userSnapshot.forEach((doc) => {
const {powerAccount,first_name,registerDate,email,company,country,phone} = doc.data();
setID(doc.data().usersID)
list.push({
usersID:doc.id,
powerAccount:powerAccount,
first_name:first_name,
registerDate:registerDate,
email:email,
company:company,
country:country,
phone:phone,
});
});
setData(list);
});
},[]);
const headers = [
// here all the keys give undefined.
{label:'User',key:data.usersID},
{label:'Account',key:data.powerAccount},
{label:'Name',key:data.first_name},
{label:'RegistrationDate',key:data.registerDate},
{label:'Email',key:data.email},
{label:'Company',key:data.company},
{label:'Country',key:data.country},
{label:'Phone',key:data.phone},
];
const csvReport = {
filename:"userReport.csv",
headers:headers,
data: data // also my data useState is undefined, although it holds values and i can see them in my table
}
return (
<CSVLink {...csvReport} >
Export
</CSVLink>
)
CodePudding user response:
You should all state coordination / update to useState
and useEffect
hooks and avoid relying on any field update outside the scope of these.
You should then remove the list
variable, move state update to your effect hook and consolidate all users data in the same structure:
const [data, setData] = useState([]);
useEffect(() => {
firebase.firestore()
.collection("Users")
.get()
.then((userSnapshot) => {
const usersData = [];
userSnapshot.forEach((doc) => {
const { powerAccount, first_name, registerDate, email, company, country, phone, userID } = doc.data();
const userData = {
usersID: doc.id,
powerAccount: powerAccount,
first_name: first_name,
registerDate: registerDate,
email: email,
company: company,
country: country,
phone: phone,
};
const headers = [
// here all the keys give undefined.
{ label: 'User', key: userID },
{ label: 'Account', key: powerAccount },
{ label: 'Name', key: first_name },
{ label: 'RegistrationDate', key: registerDate },
{ label: 'Email', key: email },
{ label: 'Company', key: company },
{ label: 'Country', key: country },
{ label: 'Phone', key: phone },
];
const csvReport = {
filename: "userReport.csv",
headers: headers,
data: userData
}
usersData.push(csvReport);
});
setData(usersData);
});
}, []);
return (
<CSVLink {...data} >
Export
</CSVLink>
)
You may need add loading state to reflect the UI effect of data being loaded.
CodePudding user response:
According to your implementation, fetching data from firebase is async so the csvData
is getting undefined because it's not updating after a state update
Try changing your code like this and let me know if it works fine
const [data, setData] = useState({
filename: "userReport.csv",
headers: [],
data: [],
});
const [id, setID] = useState("");
const filteredList = [];
useEffect(() => {
firebase
.firestore()
.collection("Users")
.get()
.then((userSnapshot) => {
let list = [];
userSnapshot.forEach((doc) => {
const {
powerAccount,
first_name,
registerDate,
email,
company,
country,
phone,
} = doc.data();
setID(doc.data().usersID);
list.push({
usersID: doc.id,
powerAccount: powerAccount,
first_name: first_name,
registerDate: registerDate,
email: email,
company: company,
country: country,
phone: phone,
});
});
const headers = [
// I'm not sure why you need this key
// but if it's only for uniqueness
// you can replace them by unique strings like
// { label: "User", key: "user" },
// { label: "Account", key: "account" },
{ label: "User", key: data.usersID },
{ label: "Account", key: data.powerAccount },
{ label: "Name", key: data.first_name },
{ label: "RegistrationDate", key: data.registerDate },
{ label: "Email", key: data.email },
{ label: "Company", key: data.company },
{ label: "Country", key: data.country },
{ label: "Phone", key: data.phone },
];
const csvReport = {
filename: "userReport.csv",
headers: headers,
data: list,
};
setData(csvReport);
});
}, []);
return <CSVLink {...data}>Export</CSVLink>;
CodePudding user response:
I think there are two things that causes the problem that you need to understand.
Fetching data from firebase is asynchronous and might take sometime before you get the returned data while you have saved csvReport
as constant variables and set it up as React element properties. So when firebase is still loading your data and your react component is already rendered / mounted, your data
state has value of []
from default value as defined in the useState
statement. Based on your code, your csvReport
constant variable will not be receiving new data from firebase unless your app is re-rendered (enter new lifecycle and repeat). For example, switching to other tab component and go back to this component without refreshing the browser.
const csvReport = {
filename:"userReport.csv",
headers:headers, => [{ label: "User", key: undefined }, ...etc]; undefined bcs `data` is []
data: data => the value is []
}
So the simple solution is NOT to save the data as constant variable and set up the React element properties directly from your useState variable. Based on your code, I would make some changes like this.
...your previous code
const getHeaders = () => {
// Do your data manipulation using `data` in useState
// For example:
const headers = data && data.map(item => {return {id: item.id}})
return headers
}
return (
<CSVLink
filename="userReport.csv"
headers={getHeaders()}
data={data}
>
Export
</CSVLink>
)
Hope this helps and have fun making changes :)