Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle multiple radio button groups in one component in reactjs?

I'm trying to send list of selected radio button ids from multiple radio button groups on clicking send button,

My problem: I am getting selected radio button from backend , then I should be able to change the radio button and send back to backend. but when I try to change the radio button it is not working.

What I did not understand: How to handle the on change function, normally on change we can change the state but to change the state on load we should grab the values radio buttons. Finally I got struck here, not understanding how to move forward.

Here is the wireframe and code snippet:

wireframe

function CardsList(props) {
  const cards = props.cards;
  return (
    <div>
      {cards.map((card, idx) => (
       <div>
         {card.cardName}
          {
            card.options.map((lo,idx) => (
              <li key={idx}>
              <input
                 className="default"
                 type="radio"
                 name={card.cardName}
                 checked={lo.selected}
             />))
          }
         <div>
       ))}
   </div>
  );
}
//array of cards coming from the backend
const cards = [
{cardName:'card1',options:[{radioName:'card1-radio1',selected:'true'},
                          {radioName:'card1-radio2',selected:'false'}]},
  {cardName:'card2',options:[{radioName:'card2-radio1',selected:'true'},
                          {radioName:'card2-radio2',selected:'false'}]}
];
ReactDOM.render(
  <CardsList cards={cards} />,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
like image 753
sravan ganji Avatar asked Dec 28 '17 21:12

sravan ganji


People also ask

How do you handle multiple radio buttons in react JS?

You should have this in your browser: We have five radio buttons each one has a string value captured by the handleChange function when clicked. The string value needs to be changed to a number by adding the '+' sign in front of the e like so setIsRadio(+e. currentTarget. value);

Can we select multiple radio buttons at once that are grouped together?

Only one radio button in a group can be selected at the same time.

How do you group radio buttons in react?

Using a Radio Button Group with a FormSet up a form with a radio button group like this. As you can see in the above example, in the form there are three different radio buttons along with the submit button, and each radio button is attached with one onChange function, called onValueChange() .

Can radio buttons have multiple selections?

Radio buttons allow a user to select a single option among multiple options. You can set the Choice Value of each option, for each button, as well as group these buttons by giving them the same Group Name.


2 Answers

You can use an object as a lookup table that holds the group names as keys.
On each change you will need to find the relevant group with the relevant option and set the new state accordingly.

Important! - one thing to notice here, is that i changed the type of the selected property from a String to a Boolean. this will let me handle the conditions like this:

<input checked={option.selected} />

If you can't change it to a Boolean then you will need to handle the condition like this:

<input checked={option.selected === 'true'} />

Here is a running example:

//array of cards coming from the backend
const data = [
  {
    cardName: 'card1', options: [{ radioName: 'card1-radio1', selected: true },
    { radioName: 'card1-radio2', selected: false }]
  },
  {
    cardName: 'card2', options: [{ radioName: 'card2-radio1', selected: true },
    { radioName: 'card2-radio2', selected: false }]
  }
];


class CardsList extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      cards: []
    };
  }

  componentDidMount() {
    setTimeout(() => {
      // mimic an async server call
      this.setState({ cards: data });
    }, 1000);
  }

  onInputChange = ({ target }) => {
    const { cards } = this.state;
    const nexState = cards.map(card => {
      if (card.cardName !== target.name) return card;
      return {
        ...card,
        options: card.options.map(opt => {
          const checked = opt.radioName === target.value;
          return {
            ...opt,
            selected: checked
          }
        })
      }
    });
    this.setState({ cards: nexState })
  }

  onSubmit = () => { console.log(this.state.cards) };

  render() {
    const { cards } = this.state;
    return (
      <div>
        {
          cards.length < 1 ? "Loading..." :
            <div>
              {cards.map((card, idx) => (
                <ul>
                  {card.cardName}
                  {
                    card.options.map((lo, idx) => {
                      return <input
                        key={idx}
                        type="radio"
                        name={card.cardName}
                        value={lo.radioName}
                        checked={!!lo.selected}
                        onChange={this.onInputChange}
                      />

                    })
                  }
                </ul>
              ))
              }
              < button onClick={this.onSubmit}>Print Cards</button>
            </div>
        }

      </div>
    );
  }
}

ReactDOM.render(<CardsList />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
like image 176
Sagiv b.g Avatar answered Oct 06 '22 17:10

Sagiv b.g


The reason why you can't change them is because of their current checked state which you are setting here:

<input
  className="default"
  type="radio"
  name={card.cardName}
  checked={lo.selected}
/>

An approach I have used for this exact scenario is storing the component's state (from the server) in my component's state (this.state), passing the state to the element: checked={this.state.isChecked}, and updating the element's state onClick.

Example:

class CardsList extends Component {
  constructor(props){
    super(props);
    this.state = {isChecked: false};
    this.inputOnClick = this.inputOnClick.bind(this);
  }
  //fetch data from server
  fetchData(){
    fetch('/api')
      .then(res => res.json())
      //this will be our initial state
      .then(res => this.setState(res))
  }
  componentDidMount(){
    this.fetchData();
  }
  //change radio button state on click
  inputOnClick(e){
    e.preventDefault();
    //invert state value
    this.setState((prevState, props) => {isChecked: !prevState.isChecked});
  }
  render(){
    return (
      <input
        type="radio"
        checked={this.state.isChecked}
        onClick={this.inputOnClick}
       />
      )
   }
}

this answer may work with single radio button group , but i am facing problem with multiple radio buttons with in multiple radio button groups.if you see the array of cards , how does it know which radio button group it belongs to.

We can modify the state based on the radio button's name.

Let's save all of your cards in your component's state. I know the cards are retrieved from the server and will be saved using setState but I am writing it like this for visual purposes.

this.state = {cards: [
  { cardName:'card1',
  options:[
    {radioName:'card1-radio1',selected:true},
    {radioName:'card1-radio2',selected:false}
   ]
  },
  { cardName:'card2',
    options:[
      {radioName:'card2-radio1',selected:true},
      {radioName:'card2-radio2',selected:false}
     ]
    }
]}

Now when we click on a radio button, we will use that radio button's name to update the state where it needs to be updated. Since React state needs to be immutable, we will create a deep copy of the state, modify it, and then set the state with it.

inputOnClick(e){
  e.preventDefault();
  var thisRadioBtn = e.target.name;
  //make a deep copy of the state
  const stateCopy = JSON.parse(JSON.stringify(this.state.cards));
  //go through state copy and update it
  stateCopy.forEach(card => {
    card.options.forEach(option => {
      if(option.radioName === thisRadioBtn){
        //invert value
        //make sure the values are booleans
        option.selected = !option.selected;
      }
    });
  });
  //update the components state
  this.setState({cards: stateCopy});
}
like image 36
Josan Iracheta Avatar answered Oct 06 '22 19:10

Josan Iracheta