I have several exercise objects that the user can join by clicking the button, and the Sidebar
component will display the number of different exercises that the user joined. Thus, the number of exercises that the user joined should only increment if the user hasn't already joined the exercise.
App.js
class App extends React.Component {
constructor() {
super();
this.state = {
//array of exercise objects from json file
exercises: data.exercises,
//exercise objects that user joined
joined: [],
}
}
//gets passed as a prop and called when user clicks join exercise button
incrementCount = (exerciseChosen) => {
let joined = this.state.joined.slice()
let exercises = this.state.exercises.slice()
exercises.forEach((exercise) => {
if (exercise._id === exerciseChosen._id) {
if (!joined.includes(exercise)) {
exercise.count++;
joined.push({ exercise })
console.log('after push joined: ' + joined.length + ' exercise count: ' + exercise.count)
}
}
})
this.setState({ exercises: exercises, joined: joined })
console.log('after setstate joined: ' + this.state.joined.length)
}
...
The expected behavior was that if the user hasn't already joined the exercise (the exercise isn't in the joined
array), it gets added to the joined
array and exercise.count
(number of people who joined exercise) gets incremented when the join button is clicked. However, when I click on the join button for the same exercise multiple times, the length of the joined
array still goes up. I tried to check if the state gets updated correctly by logging the length of the joined
array after calling setstate()
, and it seems to be logging out the length of the joined
array prior to calling to setstate()
, as shown in the attached screenshot. I'm confused about why this is happening and how to fix it. Thanks!
--After clicking on the join button 5 times-----
I added a couple comments to the code. One, is that you are directly mutating state, which should be avoided. and Second, you are pushing a new, nested object to joined so that is throwing it off.
class App extends React.Component {
constructor() {
super();
this.state = {
//array of exercise objects from json file
exercises: data.exercises,
//exercise objects that user joined
joined: [],
}
}
//gets passed as a prop and called when user clicks join exercise button
incrementCount = (exerciseChosen) => {
let joined = this.state.joined.slice()
let exercises = this.state.exercises.slice()
exercises.forEach((exercise) => {
if (exercise._id === exerciseChosen._id) {
if (!joined.includes(exercise)) {
/* You are directly mutating state here with `exercise.count++`,
although exercises is a new array, each exercise object is
still a reference to the same objects in state */
exercise.count++;
/* here I think you want `joined.push(exercise)` */
joined.push({ exercise })
console.log('after push joined: ' + joined.length + ' exercise count: ' + exercise.count)
}
}
})
this.setState({ exercises: exercises, joined: joined })
console.log('after setstate joined: ' + this.state.joined.length)
}
...
Here's what I would suggest instead:
class App extends React.Component {
constructor() {
super();
this.state = {
//array of exercise objects from json file
exercises: data.exercises,
//exercise objects that user joined
joined: [],
}
}
//gets passed as a prop and called when user clicks join exercise button
incrementCount = (exerciseChosen) => {
let joined = this.state.joined.slice()
let exercises = this.state.exercises.map((exercise) => {
if (exercise._id === exerciseChosen._id) {
if (!joined.find(e => e._id === exercise._id)) {
exercise = {...exercise, count: exercise.count + 1}
joined.push(exercise)
console.log('after push joined: ' + joined.length + ' exercise count: ' + exercise.count)
}
}
return exercise;
})
this.setState({ exercises: exercises, joined: joined })
console.log('after setstate joined: ' + this.state.joined.length)
}
...