9

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>

3 Answers 3

4

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>

Sign up to request clarification or add additional context in comments.

Comments

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});
}

3 Comments

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.
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.
@user8208248 as long as you're saving the fetched data to your state, you will be good.
2

In June 2022, I'm facing a similar issue with you. My solution is just add tag <form> on both sections with no OnSubmit or action on it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.