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

16,602

Solution 1

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>

Solution 2

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});
}
Share:
16,602
sravan ganji
Author by

sravan ganji

Don't aspire to make a living , aspire to make a change.

Updated on June 11, 2022

Comments

  • sravan ganji
    sravan ganji almost 2 years

    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>
  • sravan ganji
    sravan ganji over 6 years
    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.
  • sravan ganji
    sravan ganji over 6 years
    what if we have a dynamic state and we dont know how many cards will get from backend ? i am trying out at my end. probably i will post once i get the perfect one.
  • Josan Iracheta
    Josan Iracheta over 6 years
    @user8208248 as long as you're saving the fetched data to your state, you will be good.