Error: Too many re-renders , when modifying an array - arrays

Getting error when modifying array through large number of iterations.
data.logData1[0].data.map((values, index) => {
var result = {};
data.logData1[0].mnemonicList
.split(",")
.forEach((key, i) => (result[key] = values.split(",").map(Number)[i]));
setGraphData([...graphData, result]); //Modifying Array (here comes trouble)
});

Its difficult to say without code component, but I suspect that the problem lies in the fact that you are calling your state setter immediately inside the function component body, which forces React to re-invoke your function again, with the same props, which ends up calling the state setter again, which triggers React to call your function again.... and so on.
const resultData = data.logData1[0].data.map((values, index) => {
var result = {};
data.logData1[0].mnemonicList
.split(",")
.forEach((key, i) => (result[key] = values.split(",").map(Number)[i]));
return result
});
// somewhere in your useEffect or in function
setGraphData([...graphData, resultData]);

A work around can be you create a temporary variable and use it store the result from the loop and when are done looping, you can setGraphData to the final result
const tempVar = []
data.logData1[0].data.map((values, index) => {
var result = {};
data.logData1[0].mnemonicList
.split(",")
.forEach((key, i) => (result[key] = values.split(",").map(Number)[i]));
tempVar.push(result) //storing results to temporary array
});
setGraphData(tempVar); //setting the final result of the loop to graphData

Related

Is the insertion order for an array every going to be changed after performing a map

If I have an array and I want to call map() to that array. Will the result returned from map ever going to have different order from array?
const array = [1,2,3,4,5,6,7,8,9];
const mappedArray = array.map((num) => {
return num * num;
})
console.log(mappedArray);
The mappedArray will ever be [1,4,9,16,25,36,49,64,81]
or it is possible that it changes to something else (not the same order)?
What if I make an API call inside the map like the following
const mappedArray = array.map(async (num) => {
return await api(num);
})
// remote API
function api(num) {
return dynamoDbClient.getItem(num);
}
No, map will always iterate through the array in order. Unless the array is changed elsewhere in your code, the order of the resulting array will not change. The output will always be the same if the input is the same.
Run this code 1000 times and the array will be the same every time.
const array = [1,2,3,4,5,6,7,8,9];
const mappedArray = array.map((num) => {
return num * num;
})
console.log(mappedArray);

Can anyone explain why my state is getting updated even when i dont set it manually

So I just spent an hour debugging this code and finally got it to work, but I would want to know why this happened in the first place. I have a function that takes a value from my state, operates on it and saves the output in another variable in the state. This is the fuction:
getFolderNames = async () => {
const promises = this.state.rows.map(async item => {
if (item[".tag"] == "folder" && item.name.length > 20) {
item.name = await getFolderName(item.name);
return item;
} else return item;
});
const result = await Promise.all(promises);
this.setState({
rowsToDisplay: result
});
};
when i run this function, it was updating both the rows and rowsToDisplay to the result variable when i was only calling setState on only one of them.
Changing the function as below solves the issue but I would like to know why.
getFolderNames = async () => {
const promises = this.state.rows.map(async item => {
if (item[".tag"] == "folder" && item.name.length > 20) {
let item2 = {
...item
};
item2.name = await getFolderName(item.name);
return item2;
} else return item;
});
const result = await Promise.all(promises);
this.setState({
rowsToDisplay: result
});
};
It's because of how JavaScript handles variables. When you set a variable to an array or object, it doesn't make a new object but rather just references the original array/object.
As such, if you set a variable to equal some object, and then set a property of that variable, the original object will also be updated. Check this snippet for an example.
var foo = {changed: false};
var bar = foo;
bar.changed = true;
console.log("foo", foo.changed)
console.log("bar", bar.changed)
You can read more about the subject here: https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0
I hope this helps you in the future, since I know I also spent many hours banging my head against exactly the sort of cases you described in your original question.

Reactjs waiting for array.forEach completion before continued with callback

I've only been using JS and React for a short time, and am running into issues with waiting for a forEach loop to complete before continuing.
The function glitchLib below should pull an array of img sources from state, iterate through the elements of the array and "glitch" each image (the actual process of glitching is done with a javascript library). For each glitched image, I want to push a 2-elem array with the original source and glitched source into currentSaved[], and then pass the array of arrays in a callback.
glitchLib() {
const currentSaved = [];
var array = this.state.originalFiles;
array.forEach(function(src) {
var originalImage = src;
const image = new Image();
image.src = src;
image.onload = () => {
glitch()
.fromImage(image)
.toDataURL()
.then((dataURL) => {
const dataArray = [originalImage, dataURL];
currentSaved.push(dataArray);
});
};
});
this.props.callback(currentSaved);
}
If I wrap the callback in a setTimeout for ~10 seconds or so, the array is properly iterated through so there isn't any issue with the way the js library is performing the "glitching", which should just return a base64 image encoding. Without the setTimeout, an empty array is passed.
What is the proper way to wait for the array to be fully iterated through (or for that matter, is there any better way of doing this sort of thing)?
You can wait for the completion of a number of Promises using Promise.all():
const glitch = () => Promise.resolve('xyz')
function glitchLib(callback) {
const promises = []
const array = ['abc', 'def']
array.forEach(src => {
const originalImage = src
const image = new Image()
image.src = src
/*image.onload = */;(() => {
promises.push(
glitch()
//.fromImage(image)
//.toDataURL()
.then(dataURL => [originalImage, dataURL])
)
})()
})
Promise.all(promises)
.then(currentSaved => callback(currentSaved))
}
glitchLib(x => console.log(x))

How to make an asynchronous return a promise chain within render() with componentDidMount()?

I have a button within my return in my render function that calls a function that initiates a promise chain which the result updates a state variable. However, the value is not passed through.
I tried some attempts on componentDidMount() but haven't had any luck
Here is the button within my render function
<button onClick={this.addIPFSItem}
className="btn btn-info btn-sm m-1">NewFile</button>
This calls the following function
addIPFSItem(){
var searchAddress = "0x9Cf0dc46F259542A966032c01DD30B8D1c310e05";
const contract = require('truffle-contract')
const simpleStorage = contract(SimpleStorageContract)
simpleStorage.setProvider(this.state.web3.currentProvider)
this.state.web3.eth.getAccounts((error, accounts) => {
simpleStorage.deployed().then((instance) => {
this.simpleStorageInstance = instance
return this.simpleStorageInstance.getLength(searchAddress);
}).then((accountLength) => {
var items = []
const ipfsPrefix = "https://ipfs.io/ipfs/";
var i;
for (i = 0; i < accountLength; i++) {
var currHash = this.simpleStorageInstance.getBook(searchAddress, i,
{from: searchAddress});
var currURL = ipfsPrefix + this.currHash;
//Here I am printing the counter values, and it prints the correct
//amount
console.log('itemhash ', i)
items.push(currHash)
}
//I do not get the value of items[1] in the console, but an undefined
//promise
console.log('address URL ', items[1])
//the state of ipfsHash is not updated correctly
return this.setState({ipfsHash: items[1]});
})
})
}
Essentially, I am executing a promise chain (by connecting to web3) and retrieving data. I think the essential problem is that I am calling an async function within the render(). I'm not sure how to fix this with componentDidMount()
console.log('address URL ', items[1]) should give something like address URL 0x9Cf0dc46F259542A966032c01DD30B8D1c310e05. However, I instead get address URL Promise{<pending>}.
Your this.simpleStorageInstance.getBook is a promise, that means it is executed asynchronously.
To get it's result you either have to use .then or the new syntax async/await. With the following function your items array will be filled with the correct data :
You will have to put the async keyword before your parent function name if you choose this solution
for (i = 0; i < accountLength; i++) {
items.push(await this.simpleStorageInstance.getBook(searchAddress, i, { from: searchAddress }))
}
An even shorter syntax would imply using your raw array of accounts and map it instead of using its length :
const items = myAccounts.map(async () => await this.simpleStorageInstance.getBook(searchAddress, i, { from: searchAddress }))

Can't get the data from array react and Firestore

How can I access the value exist from an array? I think I didn't pass the array inside? Any help or advice
var isExist = this.props.isFavorite(this.props.code);
console.log(isExist)
I have this variable isExist containing the response from console below.
[]
client: [id: "LvR05w9v9xrC3r4V1W8g", exist: true]
length: 1
_proto_:Array(0)
How can I access the exist in my array? When I tried isExist[0].exist I'm getting an error. Any help?
isExist.exist = Undefined
isExist[0].exist = TypeError: Cannot read property 'exist' of undefined
favorite method where I am accessing and pushing data to the array
export const isFavorite = (data) => dispatch => {
let exist = [];
var clientQuery = firebase.firestore().collection(path).where('client_id', '==', data);
clientQuery.get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
var data = [];
data.id = doc.id;
data.exist = doc.exists;
exist.push(data)
});
});
return exist;
}
isFavorite returns a function which takes one argument dispatch and returns exist array. You seem to use async code to populate exist array. So when that function returns exist is an empty array []. You either need to continue using promises or use await. And you need to call the function returned by isFavorite.
If this.props.isFavorite and const isFavorite are not the same then add the code for this.props.isFavorite please.
You're creating an array Object. Then the array object {data}[]. So the problem is, the data is actually not only an array but also an object.
Try doing this.
var data;
data.id = doc.id;
data.exist = doc.exist;
exist.push(data);
Now you will have exist data that would be an array of Object.
Then iterate from it.
exist[0].id;
//or try
exist[0].data.id;
//Depends on how you implement your data.
Since client array doesn’t contain object with keys and values I would recommend you to try with array of index with split() to get id value and exist value from array like
Like
var isExist = this.props.isFavorite(this.props.code);
var id = isExist.client[0];
var exist = isExist.client[1];
var idValue = id ? id.split(': '): '';
console.log(idValue);
const existValue = exist ? exist.split(': '): false;
console.log(existValue);
And here change data = []; array to data ={}; object
querySnapshot.forEach((doc) => {
var data = {};
data.id = doc.id;
data.exist = doc.exists;
exist.push(data)
});

Resources