james kahn
james kahn

Reputation: 11

React Array checkbox to check only one checkbox

I have several checkbox groups on my Component of which only one of the should be selected. Each group has the same number of checkboxes (3 in the example), and the one selected is identified by the checked key inside the data list.

How can I handle this state?

class test extends Component {
  constructor(props) {
    super(props);
    this.state = {};
    const data = [
      { name: orange, checked: 2 },
      { name: apple, checked: 3 },
      { name: banana, checked: 1 }
    ];
  }

  render() {
    return (
      <>
        {data.map(items => (
          <tr>
            <td>{items.name}</td>
            <td>
              <div>
                <input type="checkbox" value="1" checked={true} />
              </div>
            </td>
            <td>
              <div>
                <input type="checkbox" value="2" checked={false} />
              </div>
            </td>
            <td>
              <div>
                <input type="checkbox" value="3" checked={false} />
              </div>
            </td>
          </tr>
        ))}
      </>
    );
  }
}

Component example

Upvotes: 1

Views: 4309

Answers (2)

guzmonne
guzmonne

Reputation: 2540

You can check at the checked option if the value of the checkbox is the same as the checked value of your item interface. Check this component as an example:

function CheckboxGroupRow({ name, checked, onUpdate }) {
  return (
    <tr className="CheckboxGroup">
      <td>{name}</td>
      <td>
        <input
          type="checkbox"
          value={1}
          checked={checked === 1}
          onChange={e => onUpdate({ name, checked: +e.target.value })}
        />
      </td>
      <td>
        <input
          type="checkbox"
          value={2}
          checked={checked === 2}
          onChange={e => onUpdate({ name, checked: +e.target.value })}
        />
      </td>
      <td>
        <input
          type="checkbox"
          value={3}
          checked={checked === 3}
          onChange={e => onUpdate({ name, checked: +e.target.value })}
        />
      </td>
    </tr>
  );
}

Each checkbox has a value, and it's checked only if the value of the checked variable matches to the one on the checkbox. For the onChange handle I have an onUpdate function that will be called with an updated data item whenever a user clicks a checkbox. Some logic upstream should handle the update.

Please take a look at this example build on CodeSandbox:

https://codesandbox.io/embed/checkboxgroup-cyv4p

I hope it helps

Upvotes: 2

Cat_Enthusiast
Cat_Enthusiast

Reputation: 15698

Try organizing your data-structure to look something like this:

  data: [
    { name: "orange", boxes: [1, 2, 3], selected: null },
    { name: "apple", boxes: [1, 2, 3], selected: null },
    { name: "pineapple", boxes: [1, 2, 3], selected: null }
  ]

That gives us a group-name, an array of values to choose from and a selected value. We'll be manipulating that data via our component-state.

Here's a codesandbox for reference: https://codesandbox.io/s/gallant-paper-b1d4z

Working code:

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: [
        { name: "orange", boxes: [1, 2, 3], selected: null },
        { name: "apple", boxes: [1, 2, 3], selected: null },
        { name: "pineapple", boxes: [1, 2, 3], selected: null }
      ]
    };
  }

  handleOnChange = e => {
    const { data } = this.state;
    const { name, value } = e.target;
    const updatedData = data.map(group => {
      if (group.name === name) {
        return {
          ...group,
          selected: group.selected === value ? null : value
        };
      } else {
        return group;
      }
    });

    this.setState({ data: updatedData }, () => console.log(this.state));
  };

  createInputGroups = () => {
    const { data } = this.state;
    const groups = data.map(group => {
      return (
        <div style={{ display: "flex" }}>
          <div>{group.name}</div>
          <div>
            {group.boxes.map(box => {
              return (
                <input
                  onChange={this.handleOnChange}
                  type="checkbox"
                  name={group.name}
                  value={box}
                  checked={group.selected == box}
                />
              );
            })}
          </div>
        </div>
      );
    });
    return groups;
  };

  render() {
    return <div>{this.createInputGroups()}</div>;
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Pleases excuse my CSS :)

Upvotes: 4

Related Questions