This.setState causes the browser to hang + Reactjs - reactjs

var columns = []
for (var key in this.props.col)
{
if (this.state.isEditing){
columns.push(<td key = {key.id}><input ref = "txt" type = "text" value = { this.state.itemValue } onChange = { this.onTextChange.bind(this) } /></td>)
}
else
{
columns.push(<td key = {key.id}>{ this.props.col[key] }</td>)
// this.setState({
// itemValue: key,
// isEditing: false
// })
console.log(key)
}
console.log(key);
}
return columns
Every time I uncomment this lines:
// this.setState({
// itemValue: key,
// isEditing: false
// })
It causes the browser to hang. And a modal message shows up on the browser which says "a script on this page may be busy" and asking me whether to stop the script or continue. Thanks.

Every time you 'setState', your component will rerender, and I guess your function is called during the rendering phase (since it seems to build columns)

Related

ReactJS: Why does navigating appear to change `readyState` of previous `EventSource`s?

Question: Why does navigating appear to change the readyState of the previous EventSources?
===============================================
Explanation: I'm working on a frontend (React) in which the user can enter a sequence of search queries (i.e. strings) and for each search query, my backend (Flask) will return a sequence of URLs. For each search query, I've decided to receive the server's response via an EventSource. Specifically, I first create a React state array of backendEventSources:
const [backendEventSources, setBackendEventSources] = useState([]);
Then I update the backendEventSources when a new prompt comes in:
useEffect(() => {
console.log('Inside useEffect')
// Take 0 for the newest prompt.
const newBackendEventSource = new EventSource(
`https://localhost:8080/generate?counter=${promptsResultsArray[0].counter}&prompt=${promptsResultsArray[0].prompt}`,
{withCredentials: false})
newBackendEventSource.addEventListener('open', () => {
console.log('SSE opened!');
});
newBackendEventSource.addEventListener('error', (e) => {
console.log('SSE error!');
console.error('Error: ', e);
});
newBackendEventSource.addEventListener('close', (e) => {
console.log('SSE closed!');
const data = JSON.parse(e.data);
console.log("close data: ", data);
newBackendEventSource.close();
});
newBackendEventSource.addEventListener('message', (e) => {
const data = JSON.parse(e.data);
console.log("message data: ", data);
// https://stackoverflow.com/a/47580775/4570472
const newPromptsResultsArray = [...promptsResultsArray];
// Since we preprend new results, we need to compute the right index from
// the counter with the equation: length - counter - 1.
// e.g., For counter 2 of a length 3 array, we want index 0.
// e.g., For counter 2 of a length 4 array, we want index 1.
// Recall, the counter uses 0-based indexing.
const index = newPromptsResultsArray.length - data.counter - 1
newPromptsResultsArray[index].URIs = [data.uri];
newPromptsResultsArray[index].isLoading = false;
setPromptsResultsArray(newPromptsResultsArray);
// Instantiating the element and setting the src property starts preloading the image.
// for (const newImgURI of newImgURIs) {
// const imageElement = new Image();
// imageElement.src = newImgURI;
// }
// setTimeout(() => {setImgURIs(newImgURIs)}, 8000);
});
// Add new backend event source to state for persistence.
setBackendEventSources(backendEventSources => [
newBackendEventSource,
...backendEventSources])
return () => {
newBackendEventSource.close();
};
}, [prompt]);
I use URL params for React navigation:
const navigateToGenerateResults = (promptString) => {
console.log('Adding new prompt results to promptsResultsArray');
// State doesn't update immediately (or even synchronously). To ensure we can immediately
// access the new values, we create a newPromptsResults.
// https://stackoverflow.com/a/62900445/4570472
const newPromptsResults = {
isLoading: true,
prompt: promptString,
counter: promptsResultsArray.length,
URIs: ["https://simtooreal-public.s3.amazonaws.com/white_background.png"]
}
// Prepend the new prompt to our promptsResultsArray
// https://stackoverflow.com/a/60792858/4570472
setPromptsResultsArray(promptsResultsArray => [
newPromptsResults, ...promptsResultsArray])
console.log('Setting prompt to: ' + newPromptsResults.prompt)
setPrompt(newPromptsResults.prompt)
console.log('Navigating from /generate to /generate with prompt: ' + newPromptsResults.prompt)
navigate(`/generate?counter=${newPromptsResults.counter}&prompt=${newPromptsResults.prompt}`)
}
However, I've discovered that as soon as I navigate from one URL to another, the previous EventSource's ready state switches from 0/1 to 2. Additionally, my newBackendEventSource.addEventListener('close' function is never triggered.
Why does navigating appear to change the readyState of the previous EventSources?

Firebase snapshot.foreach runs every time when the component get mounted in react native

I am trying to make a timeline of photos in react native with firebase. My intention is that when the user starts the app he / she has to press a button to retrieve the data. This looks like this:
in View:
<TouchableOpacity onPress={() =>this.getMessagesNew(0)}><Text>Haal berichten op</Text></TouchableOpacity>
The function:
getMessagesNew(key){
console.log('triggerd')
this.setState({
amountVideos: 0,
downloadedVideos:0,
})
this.messages = [];
var orders = firebase.database().ref('messages/'+key+'/posts').orderByChild('post').limitToLast(3);
orders.on('value', snapshot => {
snapshot.forEach( (order) => {
console.log('triggerd in loop')
let state = order.val();
if(state != null){
var newelement = {date: state.date, function: state.function,image:state.image,name: state.name, text: state.text, type:state.type, post: state.post,};
if(newelement.type == false){
this.setState({amountVideos: this.state.amountVideos+1})
this.downloadImage(newelement);
}else{
messages.push(newelement);
if(this.state.amountVideos == 0){
this.setState({
loading: false,
messagesAvailable: true,
refresh: this.state.refresh+1,
})
}else{
console.log('videos laden');
this.setState({
refresh: this.state.refresh+1
})
}
console.log('message local val'+this.messages);
console.log('message state val'+this.state.messages);
}
}else{
this.setState({
messagesAvailable: false,
})
}
});
})
}
the function: this.getMessagesNew () gets the last three messages from firebase. This works without any effort.
But the app also allows the user to upload photos to firebase. That all works, and after uploading the user is navigated back to the main screen. And here it goes wrong, the just uploaded image is already shown. While this is not the intention.
I want the user to have to press a button again to retrieve the new photos.
I know that the first part of the function is not reloaded but from the snapshot.foreach.
Is there any way to stop this process and make it happen only when a button is pressed?
you are subscribing to the changes using orders.on('value', ...) thats why things are getting synced whenever you upload an image so for achieving the behaviour get the value once from the database using orders.once('value', ...) and then everything ll work as you expect
here i m rewriting your code
getMessagesNew(key){
console.log('triggerd');
this.setState({
amountVideos: 0,
downloadedVideos:0
});
this.messages = [];
var orders = firebase.database().ref('messages/'+key+'/posts').orderByChild('post').limitToLast(3);
orders.once('value', snapshot => { // observe i have changed "on" to "once"
snapshot.forEach( (order) => {
console.log('triggerd in loop')
let state = order.val();
if(state != null){
var newelement = {date: state.date, function: state.function,image:state.image,name: state.name, text: state.text, type:state.type, post: state.post,};
if(newelement.type == false){
this.setState({amountVideos: this.state.amountVideos+1})
this.downloadImage(newelement);
}else{
messages.push(newelement);
if(this.state.amountVideos == 0){
this.setState({
loading: false,
messagesAvailable: true,
refresh: this.state.refresh+1,
})
}else{
console.log('videos laden');
this.setState({
refresh: this.state.refresh+1
})
}
console.log('message local val'+this.messages);
console.log('message state val'+this.state.messages);
}
}else{
this.setState({
messagesAvailable: false,
})
}
});
})
}

How to write array with deleted image id?

When i remove images from news i catch id, and id come to along.
How to write in array all this lonlies id ?
How to create streamIds array with streamId ?
this.state = {
mainImage: null,
mainImageUrl: "",
crop: {
aspect: 2 / 1
},
pixelCrop: null,
cropped: false,
loaded: false,
streamIds: []
};
removeImage(imageKey, streamId) {
const {singleNews} = this.props;
let streamIds = this.state.streamIds;
console.log(streamId);
singleNews.secondaryImages.splice(imageKey, 1);
if (!singleNews.secondaryImages.length) {
singleNews.secondaryImages = null;
delete singleNews.secondaryImages;
this.props.updateSingleNews(null, singleNews);
} else {
streamIds.push(streamId);
singleNews.secondaryImages.map(image => {
const index = singleNews.secondaryImages.indexOf(image);
if (index > -1) {
singleNews.secondaryImages.slice(index, 1);
FilesApi.getDocument(image.streamId).then(resp => {
singleNews.secondaryImages[index] = new File([resp], image.name, {lastModified: Date.now()});
});
}
});
this.props.updateSingleNews('streamIds', streamIds);
}
}
this is your method
If not in this func where i need to place
if you want to keep the array of ids in the same component, use
let streamIds = [];
at the top of your react component and do
removeImage (imageKey, streamId) {
console.log(streamId);
streamIds.push(streamId); // insert the item to array
}
in your removeImage method
if you want to keep the removed ids in the application state, then the concept is the same, but it need to be done on the state management tool you are using (like redux, mobx etc)

Add callback to function that is called from inside arr map method

I need to map over an array in my state and assign the returned value to another property in my state.
I then need to call a function using the value of the updated property as a parameter, wait for this function to complete and then move on to the next item in my array map method before repeating the process.
The problem is that I think my function is being run before the state has been updated through each iteration of my arr.map method.
I think I need to utilise the componentDidUpdate() method before running my function, but I am not sure how to implement that in this scenario.
Simpler to explain through code example, see below (edited for simplicity):
state = {
a: [ {name: 'some string'}, {name: 'some string'}..... ],
b: '' // empty string
}
// when button is clicked, this function is run
myFunc() {
this.state.a.map( (item) => {
this.setState({
b: item.name
})
mySecondFunc()// perform next part of the loop
})
}
mySecondFunc() {
alert( this.state.b )
}
The alert in mySecondFunc() does not return anything, it is not being updated before the function is run.
What I need to happen is that the map will get the first item from my states 'a' array, assign it to state 'b' and run mySecondFunc().
I need to wait for state to be updated, before calling mySecondFunc, and then wait for mySecondFunc() to end its function before the map gets the next item from my state's 'a' array and calls mySecondFunc again.
This is the part that I cannot figure out.
try this
state = {
a: [ {name: 'some string'}, {name: 'some string'}..... ],
b: '' // empty string
}
// when button is clicked, this function is run
myFunc(){
this.state.a.map(async(item) => {
await this.setState({
b: item.name
},async () => {
await mySecondFunc()// perform next part of the loop
})
})
}
mySecondFunc() {
alert( this.state.b )
}
//OR
myFunc(){
this.state.a.map(async(item) => {
await mySecondFunc(item)
})
}
mySecondFunc(item) {
alert( item )
}
What I have understood from the problem description is that the goal here is to pass b's value sequentially to mySecondFunc . According to my understanding of the problem, I have modified your code :
myFunc() {
this.state.a.map( (item) => {
this.setState( (prevState) => {
const modifiedName = item.name;
this.mySecondFunc(modifiedName);
return {
b: modifiedName
}
});
});
}
mySecondFunc(name) {
alert( name );
}

How can I change a data parameter in Angular 2?

I have a problem changing a data parameter in my component file:
this.commandList.ListesCommandesATransmettre.forEach(data => {
this.catalogs.forEach(catalog => {
if (catalog.Libelle === data.Catalogue) {
if (catalog.selected === false) {
console.log(data.isSelected)
data.isSelected = false;
console.log(data.isSelected)
console.log(data);
}
}
})
});
This code displays:
true
false
But the value of isSelected in console.log(data) is still true. So, why is the result false?
Try this if it's a loop problem with the callback
for (let data of this.commandList.ListesCommandesATransmettre) {
for (let catalog of this.catalogs) {
if (catalog.Libelle === data.Catalogue) {
if (catalog.selected === false) {
console.log(data.isSelected)
data.isSelected = false;
console.log(data.isSelected)
console.log(data);
}
}
}
}
I cant understand logic of your code. What happen if this.catalogs has many catalogs that meet the condition? isSelected will be setted many times in false.
Maybe this works (if I understand your logic):
this.commandList.ListesCommandesATransmettre = this.commandList.ListesCommandesATransmettre.map(data => {
const mutateData = Object.assign({}, data);
// If condition math, then isSelected set in false. Else, isSelected keep the his value
mutateData.isSelected =
this.catalogs.some(catalog => catalog.Libelle === data.Catalogue && !catalog.selected) ? false : mutateData.isSelected;
return mutateData;
})
Try this code to test if it is updating correctly. the loop will tell which are true/false and you can see if that is the case in the updated data.
this.commandList.ListesCommandesATransmettre.forEach((data, index) => {
this.catalogs.forEach(catalog => {
if (catalog.Libelle === data.Catalogue) {
if (catalog.selected === false) {
data.isSelected = false;
}
console.log(index + 'is selected: ' + data.isSelected)
}
})
});
console.log(this.commandlist);
Something I have noticed is that web browsers will only keep the the 'most current' reference of an object in memory. So after a loop has ended, the 'most current' reference is the last one to fire. So you may be looking at your last object and not the one you are trying to test. If someone else knows more, or has a reference to what is actually happening, please edit this post.

Resources