最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React Checkbox Stays Checked Even After Component is Unmounted - Stack Overflow

programmeradmin3浏览0评论

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
Add a comment  | 

3 Answers 3

Reset to default 15

Don'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 this onClick={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'));
发布评论

评论列表(0)

  1. 暂无评论