I'm creating a react js app for a project that interacts with an api. I get an array with values from the api, and those values are google maps pins. What I want to do here is: the user sets a data interval and all the pins in that data interval are shown in the map.
I have this js code to set everything:
//above is the function that receives two dates and returns the interval, not relevant here
var pinsdata = [];
useEffect(()=>{
axios.get('MYAPILINK')
.then(res=>{
var arraydatas = getDates(data1, data2)
for(var i = 0; i < res.data.data.length; i++)
{
if(arraydatas.includes(res.data.data[i].data_pg))
{
pinsdata.push(res.data.data[i])
}
}
setPorra(pinsdata)
})
}, [porra])
and for now i just have this on the return (I'm just testing this for now):
<div>
<DatePicker selected={startDate} onSelect={(date) => setStartDate(date)} dateFormat="dd-MM-y"/>
<DatePicker selected={startDate2} onSelect={(date2) => setStartDate2(date2)} dateFormat="dd-MM-y"/>
<ul>
{porra.map(pinsdata =>{
return(<li>{pinsdata.id_pg}</li>)
})}
</ul>
</div>
So... everything seems to work fine here: I set two dates and I get all the markers/pins in that data interval shown on the page.
The problem:
My page is INFINITELY re-rendering... The console.log that i have there gets spammed as soon as i refresh the page, and when i add the google map to the page, it's not even usable because the page just get's re-render every second. Now... i know that it's because of useState, but how can i get this going without use state? Because the api request is async, so i have to re-render when i have the answer from the api...
You dependencies array is incorrect you should have data1 and data2 in there instead -
var pinsdata = [];
useEffect(()=>{
axios.get('MYAPILINK')
.then(res=>{
var arraydatas = getDates(data1, data2)
for(var i = 0; i < res.data.data.length; i++)
{
if(arraydatas.includes(res.data.data[i].data_pg))
{
pinsdata.push(res.data.data[i])
}
}
setPorra(pinsdata)
})
}, [data1, data2])
providing that you want to run this effect when data1 or data2 changes, otherwise leave the array blank, and it will only run once, when the component mounts
Try removing porra from the dependancy array and add two dates(startDate and startDate2) as the dependencies.
useEffect(()=>{
axios.get('MYAPILINK')
.then(res=>{
var arraydatas = getDates(data1, data2)
for(var i = 0; i < res.data.data.length; i++)
{
if(arraydatas.includes(res.data.data[i].data_pg))
{
pinsdata.push(res.data.data[i])
}
}
setPorra(pinsdata)
})
}, [startDate, startDate2])
when you call setPorra inside useEffect, it creates an infinite loop.
Related
When I try to test this code portion by running useEffect on the info state, I notice that the info state runs 1 time less than it should be. Can I know why?
data.length = 2
const fetchData = async function (contractAddress) {
setContractAddress(contractAddress)
const loan = await readLoanAmount({"address": contractAddress});
setLoanAmount(loan)
const collected = await readCollectedAmount({"address": contractAddress});
setCollectedAmount(collected);
setInfo([...info, {"contractAddress":contractAddress,"loanAmount": loanAmount, "collectedAmount":collectedAmount}])
}
useEffect(() => {
checkWalletIsConnected();
fetch('http://localhost:5000/loanContract/getloanContracts').then((response) => response.json()).then((data) => {
for(let i =0 ; i< data.length; i++){
let ca = data[i]["contractAddress"];
fetchData(ca);
}
}).catch((err) => {
console.log(err.message);
});
}, []);
Whenever we supply an empty array to the second argument of useEffect, the effect runs only once after render. That is by definition how React built the hook.
Link to useEffect expalaination in React Docs Beta: https://beta.reactjs.org/learn/synchronizing-with-effects
Did you mean for loop inside your useEffect is running once only instead of twice (as it should for data.length = 2)?
It could be due to the way you have used setInfo setter. Instead of getting the info value directly, do the following:
setInfo(prevInfo => {
return [
...prevInfo,
{
"contractAddress":contractAddress,
"loanAmount": loanAmount,
"collectedAmount":collectedAmount
}
];
});
Receiving prevInfo this way makes sure you have the latest value of info especially when dealing with synchronous consecutive calls to the setter function of useState. This might solve the issue you're facing...
I am making a get request to an API that is linked to my database.
dataApi is a really big object with a lot of objects and arrays nested within it.
Some of the entries from the database are not having the full details that I need so I am filtering them to only show those with a length of > 5.
Now the issue is when I try to get the name of each entry which is split into either Tag1, Tag2 or Tag3.
Before this when I was accessing all the entries and getting the items within them there was no issue.
But when I try to filter them by the name and store the objects corresponding to that name in its state this issue arrises.
Edit:
When I console.log(arr1) it shows all the data but the moment I set the state to it it causes the error.
// Data from all entries in database
const [dataApi, setDataApi] = useState();
// Data for each of the tags
const [tag1, setTag1] = useState();
const [tag2, setTag2] = useState();
const [tag3, setTag3] = useState();
useEffect(() => {
axios.get(URL).then((res) => {
const data = res.data;
setDataApi(data);
});
}, []);
const getTagDetails = data => {
const arr1 = [];
const arr2 = [];
const arr3 = [];
data &&
data.forEach(d => {
// Entries into the database which do not have any tag information
// have a size of 5 and those with all the details have a size of 6
const sizeOfObject = Object.keys(d).length;
// Only need database items with all the details
if (sizeOfObject > 5) {
const name = d["tags"]["L"][0]["M"]["name"]["S"];
// Split the data for the tags into their respective name
// Will be used to set individual datasets for each tag
if (name === "Tag1") {
arr1.push(d);
}
if (name === "Tag2") {
arr2.push(d);
}
if (name === "Tag3") {
arr3.push(d);
}
}
});
setTag1(arr1);
setTag2(arr2);
setTag3(arr3);
};
getTagDetails(dataApi);
I guess the problem is you call getTagDetails(dataApi) inside of file so it causes this infinite rendering problem
Instead remove getTagDetails and try to call this functions after API resolved.
useEffect(() => {
axios.get(URL).then((res) => {
const data = res.data;
getTagDetails(data)
});
}, []);
I think your problem is the way you have structured your getTagDetails function. Each time you render, you call getTagDetails() and the first thing you do is create a new array for each tag. When you call setTag with the new array, it will rerender. You'll probably want to move the getTagDetails logic into the effect so it only runs once on mount (or add a dependency to the dependency array if you need to update on new data)
async function getData(ret) {
var list = [];
const b = await firebase.database().ref('tesco').limitToFirst(20).on('value',function (snapshot) {
snapshot.forEach(function (childSnapshot) {
list.push(childSnapshot.val())
I have to push data from firebase , to Array. It takes alot of time and i have to refresh App to see data on Screen.
put the list or return of getData() in a state, so every time it has new value the component will refreshed automatically
I come from C++, C, Python space and I'm new to react native / JS / back-end world.
I have some issues loading data from firebase. Here is what I want :
My Database :
users : uid : postids[]
posts : postids : content
I want to load the postids[] array from a user and then, load content of every postids[] in this array (according to every postids in the postids[] array).
Here is my code :
_getPostsFromDatabase() {
var docRef = firebase.firestore().collection("users").doc(firebase.auth().currentUser.uid);
return docRef.get().then(function(doc) {
if (doc.exists) {
return doc.data()["posts"];
}
}).catch(function(error) {
alert("Error getting document:", error);
});
}
_loadPosts() {
var new_posts = [];
this._getPostsFromDatabase()
.then(res => {
var i;
for (i = 0; i < res.length; i++) {
firebase.firestore().collection("posts").doc(res[i])
.onSnapshot(function(doc) {
new_posts.push(doc.data());
console.log(new_posts); --> This line print correct data
});
}
})
.catch(error => console.log(error));
console.log(new_posts); ---> This line print an empty array
}
componentDidMount() {
this._loadPosts()
}
So I want this behavior :
In componentDidMount I begine the routine --> this works
loadPosts is loading the postids[] array with _getPostsFromDatabase() function --> this works
Then, I make a for loop to push every object in an array to set the state at the end --> FAIL
At step 3, everything f... up, I made some console log to debug but there is a huge real time issue because evrything is printed randomly.
How can I get my new_posts filled array at the end of the for loop to setState. Maybe I'm wrong with this method, or if I'm not, I must have some issues with Async funtion ?
Is there an expert to help me understund better what is inside this kind of use case ?
Thanks
Basically the problem is that you are trying to perform an asynchronous code in a synchronous way.
You solution might be waiting for all promises to resolve.
_loadPosts() {
this._getPostsFromDatabase()
.then(res => {
let promises = res.map(id => {
return firebase.firestore().collection("posts").doc(id)
.get().then(doc => doc.data())
})
Promise.all(promises).then(res => {console.log(res);})
}
Your console will log before the for loop, that's the reason you are getting an empty array just include your console in the response just like this:
this._getPostsFromDatabase()
.then(res => {
var i;
for (i = 0; i < res.length; i++) {
firebase.firestore().collection("posts").doc(res[i])
.onSnapshot(function(doc) {
new_posts.push(doc.data());
console.log(new_posts); --> This line print correct data
});
}
console.log(new_posts); ---->Include here
})
Hope this helps!
I'm currently building a CSV file loader which parses the data into a JSON, It will have three different buttons, the first one which is the one that let me pick a file from my computer and loads the CSV data to convert it to JSON, after that I need to display a second button that will allow me to connect to a Shopify API, when this button is clicked it will make a get request to the API to get the JSON data and match the ID's with the file I uploaded, after that happens it will store all those ID's that match and here is when the Third button has to be displayed, this is the last button that needs to make a POST request to another API. I only need to know the button functionality, like, how can I display one, store the data and display the next one and so on.
Since I'm new into coding, I'm kind of lost right now, I've been thinking of setting the state for this and hide the buttons (second and third), the thing is that I don't know how to make a button displayed after data is loaded and not just when the button is clicked.
I'm using the react-file-reader NPM module to make the render from CSV to JSON.
`export default class csvLoader extends Component {
constructor(props) {
super(props);
}
handleFiles = files => {
let reader = new FileReader();
reader.onload = function(e) {
// Use reader.result
let csv = reader.result;
let lines = csv.split("\n");
let result = [];
let headers = lines[0].split(",");
for (let i = 1; i < lines.length; i++) {
let obj = {};
let currentLine = lines[i].split(",");
for (let j = 0; j < headers.length; j++) {
obj[headers[j]] = currentLine[j];
}
result.push(obj);
}
//return result; //JavaScript object
result = JSON.stringify(result); //JSON
console.log(result);
};
reader.readAsText(files[0]);
};
render() {
return (
<ReactFileReader handleFiles={this.handleFiles} fileTypes=".csv"}>
<button className="btn btn-primary">Importar CSV</button>
</ReactFileReader>
);
}
}`
There are no error messages, all data is being displayed in the console, Info doesn't have to be displayed on the console, but stored to be compared with the other API's information.
Thanks in advance!
I believe the best solution would be to create an action for your api call pass your data to redux and make sure that your component is listening for changes for that particular data entity in store, assuming you're using redux. If not then handling the status of your buttons via the component state would be optimal as well.