parse data from firebase in react render - javascript

I'd like to parse data I receive from componentdidmount to render.
I have this state:
constructor(props) {
super(props);
this.state = {
loading: true,
data: []
}
}
and this componentdidmount:
componentDidMount() {
var post = [];
var feedRef = firebase.database().ref().child('posts').limitToLast(10);
feedRef.once('value', async (snapshot) => {
post.push(
Object.assign(snapshot.val(), {
key: snapshot.key,
user: snapshot.user,
img: snapshot.img
})
)
this.setState({ data: post, loading: false });
console.log(this.state.data); // has the data
});
}
and this to parse the data:
{this.state.data.map(post => {
return(
<div>
<img src={post.img} />
</div>
)
})}
The problem is, although I have data in state it is not being parse in render. Any ideas what is wrong?
I also have this error: index.js:1 Warning: Each child in a list should have a unique "key" prop.
my data is wrong:
the console.log:
Array(1)
0:
-M7Y4RJMl1pd4ynwXPYJ: {img: "https://", user: "josh", userid: "T87u4DL82IaGO9X"}
-M7Y4RJMl1pdwXPYJ: {img: "https://", user: "josh2", userid: "T87u82IaGO9X"}
-M7Y4RXPYJ: {img: "https://", user: "josh3", userid: "T87u4DL82GO9X"}
-M7Y4RJMl1XPYJ: {img: "https://", user: "josh4", userid: "T87uaGO9X"}
img: undefined
key: "posts"
user: undefined

check the docs https://firebase.googleblog.com/2014/04/best-practices-arrays-in-firebase.html
You need to convert the object into an array, the map function in the render is expecting an array, but you are using an object
var obj = snapshot.val();
var arrayPosts = Object.keys(obj).map(function(key) {
return {
key,
user: obj[key].user,
img: obj[key].img
}
});
post = arrayPosts

Related

How to parse a property of a react object

I am working in react and have a resonse ( ReviewerService.getReviewers()) that returns an array of values:
0: {id: 1, firstName: 'John', lastName: 'Doe', email: 'johndoe#aol.com', responses: '{"q1":"yes","q2":"no","q3":"yes","rating":4}'}
1: {id: 2, firstName: 'bob', lastName: 'jefferson', email: 'bob#aol.com', responses: '{"q1":"bob","q2":"yes","q3":"yes","rating":5}'}.
If this.state = { reviewers: [] }.
How do i pass the response data into reviewers and parse the responses property at the same time? Therefore, then I can access these properties of the responses easily.
class ListReviewsComponent extends Component {
constructor(props) {
super(props);
this.state = {
reviewers: [],
};
}
async componentDidMount() {
await ReviewerService.getReviewers().then((res) => {
this.setState({ reviewers: res.data });
});
this.setState({ reviewers.responses: JSON.parse(this.state.reviewers.responses)}); // error
}
can this work
async componentDidMount() {
try {
const res = await ReviewerService.getReviewers();
// got the data
const reviewers = res.data;
// not parse the responses for each reviewer
const mappedReviewers = reviewers?.map(reviewer => {
try {
const parsedResponses = JSON.parse(reviewer.responses)
// do you need to convert parsedResponses to an array ??
return {
...reviewer,
responses: parsedResponses
}
} catch(error) {
return {
...reviewer,
responses: [] //
}
}
});
this.setState({ reviewers: mappedReviewers})
} catch (error) {
// log errors
}
}
Hope this helps you to sort out the issue
I think that you array returned by
ReviewerService.getReviewers()
should be in json format, treated or parsed, before, setted in setState:
data = [ {id: 1, firstName: 'John', lastName: 'Doe', email: 'johndoe#aol.com', responses: '{"q1":"yes","q2":"no","q3":"yes","rating":4}'}
,{id: 2, firstName: 'bob', lastName: 'jefferson', email: 'bob#aol.com', responses: '{"q1":"bob","q2":"yes","q3":"yes","rating":5}'} ];
Then you do this, putting array in a json treated object format
async componentDidMount() {
await ReviewerService.getReviewers().then((res) => {
this.setState({ reviewers: res.data });
});
When you do:
this.setState({ reviewers: res.data });
You area putting on this.state.reviewers, all list and all objects nested in.
You could access this.state on this component like this method below:
getResponsesOfReviewersOnArrayByIndex = (index) => {
return this.state.reviewers[index].responses
}
Or just in some method access,
this.state.reviewers[i].firstName
You can try this to understand better the JSON parse function:
const reviewersData = '[{"name":"neymar"}, {"name":"junior"}]';
const reviewers = JSON.parse(reviewersData);
console.log(reviewers[0].name);
In this W3Schools to see more examples of JSON.parse()
Hope this helps.

firebase pass data to outside variable react typescript

I am trying to pass some data to a variable in react + typescript
here is what I am trying
const data = useMemo<DummyDataType[]>(
() => [
{
userId: "Foo",
flowAmount: "Bar",
},
],
[]
);
const arr: { userId: string; flowAmount: string }[] = [];
var leadsRef = db.ref('users');
leadsRef.on('value', function(snapshot) {
snapshot.forEach(item => {
arr.push({userId: 'test', flowAmount: 'test'})
})
});
const Table = useTable<DummyDataType>({
columns: columns,
data: data,
isDataLoading: false,
isDataLoadingError: false,
isRowSelectable: false,
isMultiRowSelectable: false,
});
return (
<Box p="4" color="white" bg="rgb(35, 36, 41)">
<Button colorScheme='yellow' > Save changes</Button>
{Table}
</Box>
);
so I want to push the arr to the data variable (the arr is just some dummy thing for me to test, i need the arr to reach the data var) now the problem is that the table loads before the firebase loads, and it does not render the arr. I think i need to change the data or the arr to some useState but I am not sure how that would work out
If I use useState
type DummyDataType = {
userId: ReactNode;
flowAmount: ReactNode;
};
const data = useState<DummyDataType[]>(
() => [
{
userId: "Foo",
flowAmount: "Bar",
},
],
[]
);
I get the next error

Getting Error Data should be a "String", "Array of arrays" OR "Array of objects" when trying to export data to CSV in reactJS

I want to download some data that I have from my firebase firestore DB that I have listed in a table.
I am adding the data that is coming from my firestore in order to export to CSV and have a complete viewable file in my admin dashboard
But every time I try to follow the steps to download the data and export them to CSV format I get this error: "Data should be a "String", "Array of arrays" OR "Array of objects"
here is my code:
import { CSVLink } from 'react-csv';
const [data, setData] = useState([]);
const [csvData, setcsvData] = useState([]);
const list = []
const csvList = []
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,
});
const userData = {
usersID: doc.id,
powerAccount: powerAccount,
first_name: first_name,
registerDate: registerDate,
email: email,
company: company,
country: country,
phone: phone,
};
const headers = [
{ 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
}
csvList.push(csvReport)
});
setData(list);
setcsvData(csvList)
});
},[]);
return (
<CSVLink {...csvData} >
Export
</CSVLink>
)
I fixed this error by adding a conditional wrapper around my CSVLink component so that it didn't try to create that component before the data was loaded.
So, for your example, something like this could do the trick:
{csvData && (
<CSVLink {...csvData} >
Export
</CSVLink>
)}

Trying to export data to csv file from my firebase data gives undefined

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>
)
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>;
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.
I think there are two things that causes the problem that you need to understand.
Asynchronous Function
React Lifecycle
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 :)

(Multiple) Axios Post requests/params question

This is a multipart question (and coincidentally my first here on Stack!). To preface, I'm building a site with a Rails backend and a Vue.js frontend.
My problem is with an Axios POST request. I am attempting to send two POST requests with one click of the submit button. I have a "Trips" controller and a "User_Trips" controller - the later of which functions as a join to other tables in my database. In order for a newly created trip to show up, a new user_trip needs to be created too.
My trip posts just fine and shows up when I look for it in Postico, but my user_trip does not post successfully, and I think it's because I'm struggling to determine how to pass the recently created trip's id through as the param needed to create a user_trip. Here is a section of the code I'm working on from Vue.js:
<script>
import axios from "axios";
export default {
data: function() {
return {
trips: [],
errors: [],
name: "",
country: "",
state: "",
city: "",
postal_code: "",
start_date: "",
end_date: "",
image: "",
trip: this.trip
};
},
mounted: function() {
// axios.get("http://localhost:3000/api/trips").then(
// function(response) {
// console.log(response);
// this.trips = response.data.trips;
// }.bind(this)
// );
},
methods: {
submit: function() {
var params = {
name: this.name,
country: this.country,
state: this.state,
city: this.city,
postal_code: this.postal_code,
start_date: this.start_date,
end_date: this.end_date,
image: this.image
};
axios
.post("http://localhost:3000/api/trips", params)
.then(response => {
axios.get("http://localhost:3000/api/trips").then(
function(response) {
console.log(response);
this.trips = response.data.trips;
}.bind(this)
);
})
.catch(error => {
this.errors = error.response.data.errors;
});
var paramsTwo = {
trip_id: this.trip.id
};
axios
.post("http://localhost:3000/api/usertrips", paramsTwo)
.then(response => {
this.$router.go("/home");
})
.catch(error => {
this.errors = error.response.data.errors;
});
}
}
};
</script>
Here is the error message I receive in the console log:
Uncaught TypeError: Cannot read property 'id' of undefined and I'm thinking it's because I'm not selecting the right trip from the array...BUT when I look at the GET request in the log, the newly created trip doesn't show up - it's only visible my database. Any helpful suggestions are most appreciated!!
- Thanks
Figured it out! A big thanks to the helpful commenters and answerers.
<script>
import axios from "axios";
export default {
data: function() {
return {
trips: [],
errors: [],
name: "",
country: "",
state: "",
city: "",
postal_code: "",
start_date: "",
end_date: "",
image: "",
};
},
mounted: function() {
},
methods: {
submit: function() {
var params = {
name: this.name,
country: this.country,
state: this.state,
city: this.city,
postal_code: this.postal_code,
start_date: this.start_date,
end_date: this.end_date,
image: this.image
};
axios
.post("http://localhost:3000/api/trips", params)
.then(response => {
console.log(response);
this.trip = response.data;
var paramsTwo = {
trip_id: this.trip.id
};
axios
.post("http://localhost:3000/api/usertrips", paramsTwo)
.then(response => {
this.$router.go("/home");
})
.catch(error => {
this.errors = error.response.data.errors;
});
}
);
}
}
};
</script>
The code is breaking at the paramsTwo line and that's why your second post won't work. Make sure that the object returned by your API has an id property. Some DBs return a _id property instead of id.

Categories

Resources