Jefter Rocha
Jefter Rocha

Reputation: 407

Can't deselect all checkbox

I have a checkbox list and another checkbox at the top to check them all, but when I check them all, I can't clear them anymore.

class App extends React.Component {
  state = { checked: undefined }
  selectAll = ({ target: { checked } }) => this.setState({ checked })

  render() {
    const { checked } = this.state
    const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    return <ul>
      <input type='checkbox' onChange={this.selectAll} /> Check All
      {arr.map(i => <li>
        <input type='checkbox' checked={checked} />
        <span>checkbox {i}</span>
      </li>
      )}
    </ul>
  }
}

ReactDOM.render(<App />, document.getElementById('root'))
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Upvotes: 0

Views: 669

Answers (3)

ROOT
ROOT

Reputation: 11622

Over engineered but its a better solution where it handle also action if all checkboxes are selected it will check select all checkbox.

This snippet based on this

function CheckBox({name, value, tick, onCheck}) {
      return (
          <label>
              <input
                  
                  type="checkbox"
                  value={value}
                  checked={tick || false}
                  onChange={onCheck}
              />
              {value}
          </label>
      );
  }


function CheckBoxList ({options, isCheckedAll, onCheck}) {
    const checkBoxOptions = (
        <div className="checkbox-list">
            {options.map((option, index) => {
                return (
                    <CheckBox
                      key={index}
                      value={option.value}
                      tick={option.checked}
                      onCheck={(e) => onCheck(option.value, e.target.checked)}                     />
                );
            })}
        </div>
    );

    return (
        <div className="checkbox-list">
            <CheckBox
              name="select-all"
              value="ALL"
              tick={isCheckedAll}
              onCheck={(e) => onCheck('all', e.target.checked)}
            />
            {checkBoxOptions}
        </div>
    );
}

class List extends React.Component {
    //constructor(props) {
      //  super(props);

        state = {
            isAllSelected: false,
            checkList: [
                {
                    value: 1,
                    checked: false,
                },
                {
                    value: 2,
                    checked: false,
                },
                {
                    value: 3,
                    checked: false,
                }
            ]
        };
    //}

    onCheckBoxChange(checkName, isChecked) {
        let isAllChecked = (checkName === 'all' && isChecked);
        let isAllUnChecked = (checkName === 'all' && !isChecked);
        const checked = isChecked;

        const checkList = this.state.checkList.map((item, index) => {
            if(isAllChecked || item.value === checkName) {
                return Object.assign({}, item, {
                    checked,
                });
            } else if (isAllUnChecked) {
                return Object.assign({}, item, {
                    checked: false,
                });
            }

            return item;
        });

        let isAllSelected = (checkList.findIndex((item) => item.checked === false) === -1) || isAllChecked;

        this.setState({
            checkList,
            isAllSelected,
        });

    }

    render() {
        return (
          <div className="list">
            <CheckBoxList 
                options={this.state.checkList}
                isCheckedAll={this.state.isAllSelected}
                onCheck={this.onCheckBoxChange.bind(this)} 
            />
          </div>
        );
    }
}

ReactDOM.render(<List />, document.getElementById('root'))
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Upvotes: 2

Brian Thompson
Brian Thompson

Reputation: 14385

You'll need to control each checkbox in order to accomplish this. Otherwise your checkboxes only have controlled values once the select all option is selected.

I would store your checkboxes as an array of objects in state and do something like this:

class App extends React.Component {
  state = { 
    boxes: [
      {name: 0, checked: false}, 
      {name: 1, checked: false}, 
      {name: 2, checked: false}, 
      {name: 3, checked: false}, 
      {name: 4, checked: false}, 
      {name: 5, checked: false}, 
      {name: 6, checked: false}, 
      {name: 7, checked: false}, 
      {name: 8, checked: false}, 
      {name: 9, checked: false},
    ]
  }
  
  selectAll = (e) => {
   // Loop through every box and set checked to the value of the current checkbox
    e.persist();
    this.setState(prevState => ({ 
      boxes: prevState.boxes.map(value => (
        {...value, checked: e.target.checked }
      ))
    }))
  }
  
  handleCheck = (e, name) => {
    // Loop through each box and change the value of the corresponding box
    e.persist();
    this.setState(prevState => (
      { 
        boxes: prevState.boxes.map((value) => {
          if (name == value.name) {
            return {...value, checked: e.target.checked};
          }
          return value;
        })
      }
    ))
  }

  render() {
    return <ul>
      <input type='checkbox' onChange={this.selectAll} /> Check All
      {this.state.boxes.map(value => (
       <li key={value.name}>
         <input 
           type='checkbox' 
           checked={value.checked} // Now value is informed by state
           // Pass the event and name to handler
           onChange={(e) => this.handleCheck(e, value.name)} 
          />
          <span>checkbox {value.name}</span>
        </li>
      ))}
    </ul>
  }
}

ReactDOM.render(<App />, document.getElementById('root'))
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Upvotes: 3

palaѕн
palaѕн

Reputation: 73926

This happened since same state checked is used by all checkboxes and also you have not set any event handler to update the state when any of the checkboxes are clicked. Thus you are unable to clear any of the checkboxes.

Here is one solution to resolve this, but setting a separate checked state for each textbox, so that changing one doesn't affect others at all, other than when Check All is clicked.

class App extends React.Component {
  state = { checkboxes: [{id:1, checked: false}, {id:2, checked: false}, {id:3, checked: false}] }
  selectAll = ({ target: { checked } }) => {
    let { checkboxes } = this.state
    checkboxes.forEach(chk => chk.checked = checked) 
    this.setState({checkboxes})
  }
  
  select = ({ target: { value, checked } }) => {
    let { checkboxes } = this.state
    checkboxes.forEach(chk => {
       if (chk.id === +value) chk.checked = checked;
    })
    this.setState({checkboxes: checkboxes})
  }


  render() {
    const { checkboxes } = this.state
    return <ul>
      <input type='checkbox' onChange={this.selectAll} /> Check All
      {checkboxes.map(i => <li>
        <input type='checkbox' checked={i.checked} onClick={this.select} value={i.id}/>
        <span>Checkbox {i.id}</span>
      </li>
      )}
    </ul>
  }
}

ReactDOM.render(<App />, document.getElementById('root'))
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Upvotes: 2

Related Questions