Why is my array not updated in the state in React? [duplicate] - reactjs

This question already has answers here:
Why is setState in reactjs Async instead of Sync?
(8 answers)
Closed 4 years ago.
After calling this.setState the talents property still holds the old value:
onTalentModalCheckboxChange(e) {
debugger;
var talent = JSON.parse(e.target.dataset.talent);
talent.checked = !talent.checked;
if (this.maximumSelectedTalentsReached() && talent.checked) return;
const talentIndex = this.state.talents.findIndex(t => t.Id == talent.Id);
let updatedTalents = [...this.state.talents];
updatedTalents[talentIndex] = talent;
this.setState({ talents: updatedTalents });
// this method (setSearchResultsTotal) uses this.state.talents,
// but the talent that is updated here, is not updated.
// It still has the 'old' checked value
this.setSearchResultsTotal();
}
The talents property contains a list of Talent objects, which all have the checked property. My problem is that when setting the updated talent object
What am I missing here?

setState is asynchronous meaning the change doesn't happen immediately. So any function that uses state, needs to be called after the setState call completes. There are two ways to do this: use componentDidUpdate to fire off the function if this.state.talents is different than prevState.talents. Or, use the setState callback to call the function:
onTalentModalCheckboxChange(e) {
debugger;
var talent = JSON.parse(e.target.dataset.talent);
talent.checked = !talent.checked;
if (this.maximumSelectedTalentsReached() && talent.checked) return;
const talentIndex = this.state.talents.findIndex(t => t.Id == talent.Id);
let updatedTalents = [...this.state.talents];
updatedTalents[talentIndex] = talent;
// Only call this.setSearchResultsTotal after state has finished updating
this.setState({ talents: updatedTalents }, () => this.setSearchResultsTotal(); );
}

this.setState({ talents: updatedTalents }, () => {
this.setSearchResultsTotal();
});
try calling ur method like this , it will work

Related

Unable to understand setState of react js function paramters in a specific call?

setListOfPosts(curPosts => {
let newPosts = [...curPosts];
newPosts[newPosts.findIndex(p => p.id === postId)].alert = response.data;
}
});
//is curPosts an instance of array or complete array?? my listofPosts is an array of objects
Your setState call needs to return newPosts, and you're creating an array using the spread operator which is why it's coming back as an array of objects.
I'm not sure what your desired output is, but by adding a return function it will set the state:
setListOfPosts(curPosts => {
let newPosts = [...curPosts];
newPosts[newPosts.findIndex(p => p.id === postId)].alert = response.data;
return newPosts
}
});
This is untested but if your logic is correct should return an array of objects with the objects alert value updated.
Another option would be to do your logic before your setState call, by creating a a newState array and then simply updating the state with that new array without the use of the callback.
The callback function is useful if you want to add a new object to state array or do something that preserves the initial state, in your example you could do it without the callback like this:
// Create a copy of the state array that you can manipulate
const newPosts = [...newPosts]
if (data.response) {
// Add your logic to the state copy
newPosts[newPosts.findIndex(p => p.id === postId)].alert = response.data;
// Replace state with state copy
setListOfPosts(newPosts)
}
Again untested but hopefully this should help you understand the use of the callback function and the right way to use it.

"_mock.default.unsubscribe is not a function" React using mock.js can't unsubscribe

Will attach full example of this task mock.js unsubscribe. I have a store reducer (tbodyCommand.reducer.js) that needs to take 10 rows from data by subscribing it and than after that it have to be unsubscribed. I am using standard .unsubscribe() method, but it goes to TypeError _mock.default.unsubscribe is not a function. How can i turn off my subscribing?
I found similar question over here unsubscribe is not a function on an observable where main problem appears in RxJS version but mine seems to work good for me. The example of syntax construction over here with subscribe method doesn't contain any way to deal with mock.js; How can I use it in my example so it will work? Is there any other ways to come to this problem simpler?
method
Got changed properties$.subscribe to variable and took out return from subscribe function.
var row_temp = [];
const makeTbodyArray = () => {
properties$.subscribe((data) => { // this stuff need to go in variable
if (row_temp.length < 11) {
row_temp.push(data);
} else {
properties$.unsubscribe();
return row_temp; // taking out return
}
});
};
to
var row_temp = [];
const makeTbodyArray = () => {
let subscription = properties$.subscribe((data) => {
if (row_temp.length < 11) {
row_temp.push(data);
} else {
subscription.unsubscribe();
}
});
return row_temp; // like that
};

Angular component: How to check contents only after API call.subscribe has populated variable completely? [duplicate]

This question already has answers here:
How do I return the response from an Observable/http/async call in angular?
(10 answers)
Closed 2 years ago.
I have a component that calls on an endpoint to populate a variable of type List which is a List of PaperNames.
papers: paperNameDTO;
I populate papers on the page loading in ngOnInit:
ngOnInit() {
this.fileService.getPaperNames().subscribe(data => {
console.log('Entering here API call');
this.papers = data['papers'];
});
if(this.papers.papersList.length > 0) {
console.log('Entering filteredPapers');
const filteredPapers = this.papers.papersList.filter(paper => paper.name
!== 'FINAL_YEAR' && paper.name !== 'FIRST_YEAR' );
console.log('Final length is = ' + filteredPapers.length);
}
}
The goal is to exclude returned Strings with names 'FINAL_YEAR' and 'FIRST_YEAR' which i filter into another array of Strings.
Problem i face is this method does not work, when i check the console logs, the
loop if(this.tables.tables.length > 0) gets entered first before this.enquireService.getTableNames().subscribe(data =>. Why is that so? And how could i accomplish this otherwise?
Whenever a subscribe is used it means, sometime in the future something will happen. The code moves on to next statement where it immediately executes. If the next statement relies on a state inside the subscription it will not work. To fix, from the subscription call the function which need the data.
Any functionality that directly depends on an asynchronous data should be inside the subscription. Please see here for more details on how to access asynchronous data: https://stackoverflow.com/a/14220323/6513921
ngOnInit() {
this.fileService.getPaperNames().subscribe(data => {
console.log('Entering here API call');
this.papers = data['papers'];
const filteredPapers = this.papers.papersList.filter(
paper => paper.name !== 'FINAL_YEAR' && paper.name !== 'FIRST_YEAR');
});
}

Detect handle change inside componentDidUpdate method - Reactjs

I have a form that contains several questions. Some of the questions contains a group of subquestions.
The logic to render sub questions is written inside componentDidUpdate method.
componentDidUpdate = (prevProps, prevState, snapshot) => {
if (prevProps !== this.props) {
let questions = this.props.moduleDetails.questions,
sgq = {};
Object.keys(questions).map((qId) => {
sgq[qId] = (this.state.groupedQuestions[qId]) ? this.state.groupedQuestions[qId] : [];
let answerId = this.props.formValues[qId],
questionObj = questions[qId],
groupedQuestions = [];
if(questionObj.has_grouped_questions == 1 && answerId != null && (this.state.groupedQuestions != null)) {
groupedQuestions = questions[qId].question_group[answerId];
let loopCount = this.getLoopCount(groupedQuestions);
for(let i=0; i<loopCount; i++) {
sgq[qId].push(groupedQuestions);
}
}
});
this.setState({groupedQuestions: sgq});
}
}
The problem is that on every key stroke of text field, handleChange method is invoked which will ultimately invoke componentDidUpdate method. So the same question groups gets rendered on every key stroke.
I need a way to detect if the method componentDidUpdate was invoked due to the key press(handleChange) event so that i can write logic as follows.
if(!handleChangeEvent) {
Logic to render question group
}
Any idea on how to integrate this will be appreciated.
I assume your textfield is a controlled component, meaning that its value exists in the state. If this is the case, you could compare the previous value of your textfield to the new one. If the value is different, you know the user entered something. If they are equal however, the user did something else at which point you want your snippet to actually execute.
Basically:
componentDidUpdate = (prevProps) => {
// if value of textfield didn't change:
if (prevProps.textfieldValue === this.props.textfieldValue) {
// your code here
}
}
Another approach is to use componentDidReceiveProps(). There you can compare the props to the previous ones, similarly to the above, and execute your code accordingly. Which method is most suitable depends on how your app works.

Updating arrays in react component state

In the function below, I am trying to update the state of a react component; the animalMix item is an array. I take a copy, update it and then try to overwrite the original. I have checked that the new array (newAnimalsHeld) is updated correctly, but this is not reflected when i set animalMix in state equal to it.
The whole thing can be seen in context here:
https://codepen.io/timsig/pen/XVdbdo?editors=0010
Many thanks for any help.
removePair(){
console.log('Match!');
console.log(this.flipped);
let animalsHeld = [...this.state.animalMix];
let theMatch = this.flipped[0].props.animal;
let newAnimalsHeld = animalsHeld.map(function(animal){
if (animal.animal === theMatch) {
console.log('MATCH! Animal: ' + animal.animal + 'Match:' + theMatch);
return {};
}else{
return animal;
}
});
console.log('New Animals held: ', newAnimalsHeld);
this.setState({
animalMix: newAnimalsHeld,
doTurn: true
});
this.flipped = [];
console.log(this.state.doTurn, this.state.animalMix);
}
setState is an asynchronous function. However, you can print to console after state has updated in the following manner:
this.setState({
animalMix: newAnimalsHeld,
doTurn: true
},() => {console.log(this.state.doTurn, this.state.animalMix);});

Resources