Son Le
Son Le

Reputation: 138

Reactjs - Controlling Multiple Checkboxes

Im building a CheckAllBoxes component in Reactjs. I have a list of items

fruits = {orange, apple, grape}

A general <SelectBox /> component to display and toggle the HTML checkbox

I need to build a <Fruits /> component to list all the fruits and each of item has its own <SelectBox />

Then I need to build a <SelectAll /> component which has a <SelectBox /> and when it is checked, it will toggle all the <CheckBox /> of <Fruits />

If any fruit is unchecked again, then the <SelectAll /> should be unchecked too.

The result should look something like this:

enter image description here

How can I get the <SelectAll /> to control other checkboxes ?

Upvotes: 8

Views: 21495

Answers (2)

knowbody
knowbody

Reputation: 8276

Here is the quick example on how you could do it:

import React, { Component } from 'react';

export default class SelectBox extends Component {
  constructor() {
    super();

    this.handleClick = this.handleClick.bind(this);
    this.state = {
      allChecked: false,
      checkedCount: 0,
      options: [
        { value: 'selectAll', text: 'Select All' },
        { value: 'orange', text: 'Orange' },
        { value: 'apple', text: 'Apple' },
        { value: 'grape', text: 'Grape' }
      ]
    };
  }

  handleClick(e) {
    let clickedValue = e.target.value;

    if (clickedValue === 'selectAll' && this.refs.selectAll.getDOMNode().checked) {
      for (let i = 1; i < this.state.options.length; i++) {
        let value = this.state.options[i].value;
        this.refs[value].getDOMNode().checked = true;
      }
      this.setState({
        checkedCount: this.state.options.length - 1
      });

    } else if (clickedValue === 'selectAll' && !this.refs.selectAll.getDOMNode().checked) {
      for (let i = 1; i < this.state.options.length; i++) {
        let value = this.state.options[i].value;
        this.refs[value].getDOMNode().checked = false;
      }
      this.setState({
        checkedCount: 0
      });
    }

    if (clickedValue !== 'selectAll' && this.refs[clickedValue].getDOMNode().checked) {
      this.setState({
        checkedCount: this.state.checkedCount + 1
      });
    } else if (clickedValue !== 'selectAll' && !this.refs[clickedValue].getDOMNode().checked) {
      this.setState({
        checkedCount: this.state.checkedCount - 1
      });
    }
  }

  render() {
    console.log('Selected boxes: ', this.state.checkedCount);

    const options = this.state.options.map(option => {
      return (
        <input onClick={this.handleClick} type='checkbox' name={option.value} key={option.value}
               value={option.value} ref={option.value} > {option.text} </input>
      );
    });


    return (
      <div className='SelectBox'>
        <form>
          {options}
        </form>
      </div>
    );
  }
}

I'm sorry for the ES6 example. Will add ES5 example when I find more time, but I think you can get the idea on how to do it. Also you definitely want to break this down into 2 components. Then you would just pass your options as props to the Child component.

Upvotes: 7

Victor
Victor

Reputation: 866

I would recommend reading Communication between Components

Now in your example you have a communication between two components that don't have a parent-child relationship. In these case you could use a global event system. Flux works great with React.

In your example I would make FruitStore with the component Fruit listening to store. The FruitStore contains a list with all fruits and if they are selected or not. Fruit will saves it's content with setState().

Fruit passes to it's children their status per props. example: <CheckBox checked={this.state.fruit.checked} name={this.state.fruit.name}/>

Checkbox should fire when clicked a FruitAction.checkCheckbox(fruitName).

The FruitStore will then update the Fruit component and so on.

It take some time to get into this unidirectional architecture, but it's worth learning it. Try starting with the Flux Todo List Tutorial.

Upvotes: 0

Related Questions