0

What is the correct way to manage the state of radio and checkboxes using React?

In some instances a form would be rendered partially completed so some radio and checkboxes would be pre selected on first load.

I have the following code snippet and i cannot get it to work as expected.

    var formData = {
  "id": 13951,
  "webform_id": 1070,
  "page": 0,
  "type": "radios",
  "name": "What industry are you in?",
  "tooltip": "",
  "weight": 0,
  "is_required": 1,
  "default_value": "",
  "validation": "",
  "allow_other_option": 0,
  "other_option_text": "",
  "mapped_question_id": "a295189e-d8b4-11e6-b2c5-022a69d30eef",
  "created_at": "2017-04-07 18:40:39",
  "updated_at": "2017-04-07 18:40:39",
  "option_conditional_from": null,
  "default_value_querystring_key": "",
  "deleted_at": null,
  "is_auto_save": 0,
  "is_component_number_hidden": 0,
  "is_component_inline": 0,
  "enable_confirm_validation": 0,
  "confirm_validation_text": null,
  "additional_options": "",
  "url_mapping": "",
  "webformcomponentoptions": [
    {
      "id": 13888,
      "webform_component_id": 13951,
      "key": "Hospitality",
      "value": "Hospitality",
      "created_at": "2017-04-07 18:40:39",
      "updated_at": "2017-04-07 18:40:39",
      "group": "",
      "selected" : false
    },
    {
      "id": 13889,
      "webform_component_id": 13951,
      "key": "Retail",
      "value": "Retail",
      "created_at": "2017-04-07 18:40:39",
      "updated_at": "2017-04-07 18:40:39",
      "group": "",
      "selected" : false
    },
    {
      "id": 13890,
      "webform_component_id": 13951,
      "key": "Other",
      "value": "Other",
      "created_at": "2017-04-07 18:40:39",
      "updated_at": "2017-04-07 18:40:39",
      "group": "",
      "selected" : false
    }
  ]
}

class WebformApp extends React.Component {
  render() {
    return (
      <form>
        <label>{this.props.webform.name}</label>
        <div className="group-wrapper">
          <Radio radio={this.props.webform.webformcomponentoptions} />
        </div>
      </form>
    )
  }
}

class Radio extends React.Component {
  render() {
    var options = [];
    this.props.radio.forEach(function(radio, i) {
      options.push(<Option option={radio} key={radio.id} index={i}  />);
    })
    return (
      <div>{options}</div>
    )
  }
}

class Option extends React.Component {
  constructor(props) {
    super(props);
    this.handleOptionChange = this.handleOptionChange.bind(this);
    this.state = {selectedIndex: null};
  }

  handleOptionChange(e) {
    this.setState({selectedIndex: this.props.index}, function() {
    });
  }

  render() {
    const selectedIndex = this.state.selectedIndex;
    return (
      <div>
        <input type="radio"
          value={this.props.option.value}
          name={this.props.option.webform_component_id}
          id={this.props.option.id}
          checked={selectedIndex === this.props.index}
          onChange={this.handleOptionChange} />
        <label htmlFor={this.props.option.id}>{this.props.option.key}</label>
      </div>
    )
  }
 }

ReactDOM.render(
  <WebformApp webform={formData} />,
  document.getElementById('app')
);

https://codepen.io/jabreezy/pen/KWOyMb

2 Answers 2

1

The most important thing would be to have the Radio component handle the state, and keeping track of the selected option.

In addition, I would simplify by using map instead of forEach, and foregoing the Option component for a class method returning an <input type='radio'>. For simplicity's sake, using the option value for keeping track of the selected state instead of the index, and mimicking React's select component allowing a default value prop instead of setting each option's selected prop (which you don't seem to be using).

Finally, for order's sake, renaming the Radio:s radio prop to the (IMO) more correct options. Ergo (caveat, I haven't tested this):

class WebformApp extends React.Component {
  render() {
    return (
      <form>
        <label>{this.props.webform.name}</label>
        <div className="group-wrapper">
          <Radio options={this.props.webform.webformcomponentoptions} value={this.props.webform.value} />
        </div>
      </form>
    )
  }
}

class Radio extends React.Component {
  constructor (props) {
    super(props)

    this.handleOptionChange = this.handleOptionChange.bind(this)
    this.state = {value: this.props.value}
  }

  render() {
    return this.props.options.map(this.getOption)
  }

  handleOptionChange (e) {
    this.setState({value: e.target.value})
  }

  getOption (option) {
    return (
      <div>
        <input type='radio'
          value={option.value}
          name={option.webform_component_id}
          id={option.id}
          key={option.id}
          checked={this.state.value === option.value}
          onChange={this.handleOptionChange} />
        <label htmlFor={option.id}>{option.key}</label>
      </div>
    )
  }
}

ReactDOM.render(
  <WebformApp webform={formData} />,
  document.getElementById('app')
);
Sign up to request clarification or add additional context in comments.

Comments

0

Thank you so much for your input Linus. You set me along the correct path and i've solved my problem the following way:

var formData = {
  "id": 13951,
  "webform_id": 1070,
  "page": 0,
  "type": "radios",
  "name": "What industry are you in?",
  "tooltip": "",
  "weight": 0,
  "is_required": 1,
  "default_value": "",
  "validation": "",
  "allow_other_option": 0,
  "other_option_text": "",
  "mapped_question_id": "a295189e-d8b4-11e6-b2c5-022a69d30eef",
  "created_at": "2017-04-07 18:40:39",
  "updated_at": "2017-04-07 18:40:39",
  "option_conditional_from": null,
  "default_value_querystring_key": "",
  "deleted_at": null,
  "is_auto_save": 0,
  "is_component_number_hidden": 0,
  "is_component_inline": 0,
  "enable_confirm_validation": 0,
  "confirm_validation_text": null,
  "additional_options": "",
  "url_mapping": "",
  "webformcomponentoptions": [
    {
      "id": 13888,
      "webform_component_id": 13951,
      "key": "Hospitality",
      "value": "Hospitality",
      "created_at": "2017-04-07 18:40:39",
      "updated_at": "2017-04-07 18:40:39",
      "group": "",
      "selected" : false
    },
    {
      "id": 13889,
      "webform_component_id": 13951,
      "key": "Retail",
      "value": "Retail",
      "created_at": "2017-04-07 18:40:39",
      "updated_at": "2017-04-07 18:40:39",
      "group": "",
      "selected" : false
    },
    {
      "id": 13890,
      "webform_component_id": 13951,
      "key": "Other",
      "value": "Other",
      "created_at": "2017-04-07 18:40:39",
      "updated_at": "2017-04-07 18:40:39",
      "group": "",
      "selected" : false
    }
  ]
}

class WebformApp extends React.Component {
  render() {
    return (
      <form>
        <label>{this.props.webform.name}</label>
        <div className="group-wrapper">
          <Radio radio={this.props.webform.webformcomponentoptions} />
        </div>
      </form>
    )
  }
};

class Radio extends React.Component {
  constructor(props) {
    super(props);
    this.state = {selectedOption: 'Other'};
  }

  handleOptionChange(changeEvent) {
    this.setState({
      selectedOption: changeEvent.target.value
    })
  };

  renderOption(props) {
    return (
      <div>
        <h3>{props.index}</h3>
        <input type="radio"
          value={props.option.value}
          name={props.option.webform_component_id}
          id={props.option.id}
          checked={props.status}
          onChange={props.clickeme} />
        <label htmlFor={props.option.id}>{props.option.key}</label>
      </div>
    )
  };

  render() {
    return (
      <div>
        {this.props.radio.map(function(radio) {
          var selected = this.state.selectedOption === radio.value;
          return <this.renderOption option={radio} key={radio.value} status={selected} clickeme={(e)=> this.handleOptionChange(e)} />;
        }, this)}
      </div>
    )
  };
};

ReactDOM.render(
    <WebformApp webform={formData} />,
    document.getElementById('app')
);
<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="app"></div>

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.