So, I have a list of items with checkboxes, just like a classic todo application.
When I select "Johnny" and then click, "Delete Selected Records," Johnny does go away. Yay!
But, once Johnny is gone Renee is checked, now.
I did NOT click on Renee. I only clicked on Johnny, but once Johnny was deleted Renee became checked. wtf?
Obviously, something is happening where the checkbox DOM is staying true of checked, even though I just wiped out the checkbox in its place.
I tried solving this through updating the state in componentWillUnmount, but that didn't work.
import React, {Component} from 'react';
class Person extends Component {
constructor(props) {
super(props);
this.state = {
checked: false
}
this.checkboxToggle = this.checkboxToggle.bind(this);
}
checkboxToggle() {
this.setState({ checked: !this.state.checked }, () => {
if (this.state.checked === false) {
console.log("Checked should be FALSE");
}
else if (this.state.checked === true) {
console.log("Checked should be TRUE");
this.props.setForDelete(this.props.person._id);
}
});
}
componentWillUnmount() {
this.setState({ checked: false });
}
render() {
return (
<div>
<input type="checkbox" name="person" checked={this.state.checked} onChange={this.checkboxToggle} />
{this.props.person.name} ({this.props.person.age})
</div>
);
}
}
export default Person;
Everything else in the Person component is working fine. How do I get all the checkboxes to become unchecked once I have deleted any of the records?
UPDATE: Here's PeopleList, the component that holds all of the Persons.
import React, { Component } from 'react';
import Person from './Person';
class PeopleList extends Component {
constructor(props) {
super(props);
}
componentWillUnmount() {
this.props.resetSetForDeleteArr();
}
render() {
return(
<div>
{this.props.people.map((person, i) => {
return <Person key={i} person={person} setForDelete={this.props.setForDelete} />;
})}
<div onClick={() => this.props.deleteRecords()}>Delete Selected Records</div>
<div onClick={() => this.props.resetSetForDeleteArr()}>Empty Array.</div>
</div>
);
}
} // end class
export default PeopleList;
So, I have a list of items with checkboxes, just like a classic todo application.
When I select "Johnny" and then click, "Delete Selected Records," Johnny does go away. Yay!
But, once Johnny is gone Renee is checked, now.
I did NOT click on Renee. I only clicked on Johnny, but once Johnny was deleted Renee became checked. wtf?
Obviously, something is happening where the checkbox DOM is staying true of checked, even though I just wiped out the checkbox in its place.
I tried solving this through updating the state in componentWillUnmount, but that didn't work.
import React, {Component} from 'react';
class Person extends Component {
constructor(props) {
super(props);
this.state = {
checked: false
}
this.checkboxToggle = this.checkboxToggle.bind(this);
}
checkboxToggle() {
this.setState({ checked: !this.state.checked }, () => {
if (this.state.checked === false) {
console.log("Checked should be FALSE");
}
else if (this.state.checked === true) {
console.log("Checked should be TRUE");
this.props.setForDelete(this.props.person._id);
}
});
}
componentWillUnmount() {
this.setState({ checked: false });
}
render() {
return (
<div>
<input type="checkbox" name="person" checked={this.state.checked} onChange={this.checkboxToggle} />
{this.props.person.name} ({this.props.person.age})
</div>
);
}
}
export default Person;
Everything else in the Person component is working fine. How do I get all the checkboxes to become unchecked once I have deleted any of the records?
UPDATE: Here's PeopleList, the component that holds all of the Persons.
import React, { Component } from 'react';
import Person from './Person';
class PeopleList extends Component {
constructor(props) {
super(props);
}
componentWillUnmount() {
this.props.resetSetForDeleteArr();
}
render() {
return(
<div>
{this.props.people.map((person, i) => {
return <Person key={i} person={person} setForDelete={this.props.setForDelete} />;
})}
<div onClick={() => this.props.deleteRecords()}>Delete Selected Records</div>
<div onClick={() => this.props.resetSetForDeleteArr()}>Empty Array.</div>
</div>
);
}
} // end class
export default PeopleList;
Share
Improve this question
edited Apr 18, 2017 at 20:36
Gabriel Kunkel
asked Apr 18, 2017 at 20:18
Gabriel KunkelGabriel Kunkel
2,7617 gold badges31 silver badges49 bronze badges
2
- Can you show the component code in which the personlist is rendered? – Chris Commented Apr 18, 2017 at 20:34
- @Chris Just updated. – Gabriel Kunkel Commented Apr 18, 2017 at 20:36
3 Answers
Reset to default 15Don't use an array index as key attribute!
A key is the only prop React uses to identify DOM elements. If the key is the same as before, in your case 0
React assumes that the DOM element represents the same content as before - even if there's another text now.
So React would NOT rerender the state and update the checked property.
Use something unique instead like:
<Person key={person + '_' + i} ...
Try changing the below line in your PeopleList
{this.props.people.map((person, i) => {
return <Person key={person._id} person={person} setForDelete={this.props.setForDelete} />;
})}
I think you may be having some problems with the states and passing down the functions. I created an example that mirrors your current implementation with simple map and filter function to mimic the delete. If I have to guess you might not be keeping track of which data is checked and deleted, and probably the checked values are cached. As you can see in the code bellow here are a couple of suggestion that you can implement to make the code more easy:
- Keep the Person as a Dumb/Leaf component, after all it only renders your checkbox, do not try to keep a checked state there. (at least remove the unmount lifecycle usually you do not need it)
- You could use
context
or maybe a library that help you keep track of the states, mobx is rather simple to implement. onClick={() => this.props.deleteRecords()}>
may look better like thisonClick={this.props.deleteRecords.bind(this)}>
If you do not need any extra logic from the People List.
Take a look here and try to run it:
import React, {Component} from 'react';
import {render} from 'react-dom';
const Person = ({person, setForDelete}) => (
<div>
<input type="checkbox" name="person" checked={person.checked} onChange={setForDelete.bind(this, person)} />
{person.name}
</div>
);
class PeopleList extends Component {
render() {
return(
<div>
{this.props.people.map((person, i) => {
return <Person key={i} person={person} setForDelete={this.props.setForDelete} />;
})}
<div onClick={this.props.deleteRecords}>Delete Selected Records</div>
</div>
);
}
} // end class
class App extends React.Component {
constructor(props) {
super(props)
this.state = {people:[{id:1, name:'Cesar', checked:false},{id:2, name:'Jose', checked:false},{id:3, name:'Marbel', checked:false}]}
}
deleteRecords() {
const people = this.state.people.filter(p => !p.checked);
this.setState({people});
}
setForDelete(person) {
const checked = !person.checked;
const people = this.state.people.map((p)=>{
if(p.id === person.id)
return {name:person.name, checked};
return p;
});
this.setState({people});
}
render () {
return <PeopleList people={this.state.people} deleteRecords={this.deleteRecords.bind(this)} setForDelete={this.setForDelete.bind(this)}/>;
}
}
render(<App/>, document.getElementById('app'));