BlackH3art
BlackH3art

Reputation: 596

How to setup form with multiple Checkboxes which only one can be "checked" with Material-UI components?

I want to build simple form using Material-UI which has:

And then, basing on which option is checked I want to push certain information to my formData and send it to my backend on handleSubmit

My Form component looks like this:

const MyFormComponent= () => {

  const [formData, setFormData] = useState({ reason: "", feedback: ""});
  const [checkboxesState, setCheckboxesState] = useState({ 
    one: false,
    two: false,
    three: false 
  })

  const handleSubmit = (e) => {
    /* ... */
  };

  const handleChange = (e) => {
    /* ... */
  }

  return ( 
    <>
      <form onSubmit={handleSubmit}>

        <Grid container>

          <FormLabel component="legend"> Some asked question here? </FormLabel>
          <FormGroup>
            <FormControlLabel
              control={<Checkbox checked={checkboxesState.one} color="primary" onChange={handleChange} name="one" />}
              label="Label for my first checkbox"
            />
            <FormControlLabel
              control={<Checkbox checked={checkboxesState.two} color="primary" onChange={handleChange} name="two" />}
              label="Label for my secound checkbox"
            />
            <FormControlLabel
              control={<Checkbox checked={checkboxesState.three} color="primary" onChange={handleChange} name="three" />}
              label="Label for my third checkbox"
            />
          </FormGroup>
        </Grid>

        <ButtonWrapper>
          <Button variant="outlined" color="secondary" type="submit"> Delete </Button>
          <Button variant="contained" color="primary"> Go back </Button>
        </ButtonWrapper>

      </form>
    </>
   );
}

So I came up with solution on my handleChange like this, works perfectly fine, but I will have to create another if(/* */) else if(/* */) else if(/* */) instruction on my handleSubmit.

I believe there is much more elegant solution based on Material-UI components API which I don't know yet or don't know how to use it. Can someone suggest me something else in this situation?

  const handleChange = (e) => {
    if(e.target.name === "one") {
      setCheckboxesState({
        one: e.target.checked,
        two: false,
        three: false
      });
    } else if (e.target.name === "two") {
      setCheckboxesState({
        one: false,
        two: e.target.checked,
        three: false
      });
    } else if (e.target.name === "three") {
      setCheckboxesState({
        one: false,
        two: false,
        three: e.target.checked
      });
    }
  }

Upvotes: 2

Views: 4275

Answers (2)

shobhit1
shobhit1

Reputation: 654

If you want to achieve "Multiple options, only one selected", Radio Buttons is a better UX choice for that scenario.

Quoting from https://www.nngroup.com/articles/checkboxes-vs-radio-buttons/

Radio buttons are used when there is a list of two or more options that are mutually exclusive and the user must select exactly one choice. In other words, clicking a non-selected radio button will deselect whatever other button was previously selected in the list.

Checkboxes are used when there are lists of options and the user may select any number of choices, including zero, one, or several. In other words, each checkbox is independent of all other checkboxes in the list, so checking one box doesn't uncheck the others.

To achieve Radio group using Material UI, please refer to Material UI docs. They have excellent examples.

import React from 'react';
import Radio from '@material-ui/core/Radio';
import RadioGroup from '@material-ui/core/RadioGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormControl from '@material-ui/core/FormControl';
import FormLabel from '@material-ui/core/FormLabel';

export default function RadioButtonsGroup() {
  const [value, setValue] = React.useState('female');

  const handleChange = (event) => {
    setValue(event.target.value);
  };

  return (
    <FormControl component="fieldset">
      <FormLabel component="legend">Gender</FormLabel>
      <RadioGroup aria-label="gender" name="gender1" value={value} onChange={handleChange}>
        <FormControlLabel value="female" control={<Radio />} label="Female" />
        <FormControlLabel value="male" control={<Radio />} label="Male" />
        <FormControlLabel value="other" control={<Radio />} label="Other" />
        <FormControlLabel value="disabled" disabled control={<Radio />} label="(Disabled option)" />
      </RadioGroup>
    </FormControl>
  );
}

Upvotes: 3

Domino987
Domino987

Reputation: 8774

You could track the current select checkbox with one variable instead of multiple.

const MyFormComponent= () => {

const [formData, setFormData] = useState({ reason: "", feedback: ""});
const [checkboxesState, setCheckboxesState] = useState(-1)

const handleSubmit = (e) => {
  /* ... */
};

 const handleChange = (e) => {
  setCheckboxesState(e.target.name);
 }

 return ( 
<>
  <form onSubmit={handleSubmit}>

    <Grid container>

      <FormLabel component="legend"> Some asked question here? </FormLabel>
      <FormGroup>
        <FormControlLabel
          control={<Checkbox checked={checkboxesState === 0} color="primary" onChange={handleChange} name={0} />}
          label="Label for my first checkbox"
        />
        <FormControlLabel
          control={<Checkbox checked={checkboxesState === 1} color="primary" onChange={handleChange} name={1} />}
          label="Label for my secound checkbox"
        />
        <FormControlLabel
          control={<Checkbox checked={checkboxesState === 2} color="primary" onChange={handleChange} name={2} />}
          label="Label for my third checkbox"
        />
      </FormGroup>
    </Grid>

    <ButtonWrapper>
      <Button variant="outlined" color="secondary" type="submit"> Delete </Button>
      <Button variant="contained" color="primary"> Go back </Button>
    </ButtonWrapper>

  </form>
</>
 );

}

Upvotes: -1

Related Questions