funtymeeee
funtymeeee

Reputation: 43

React JS Radio input state

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

Upvotes: 0

Views: 454

Answers (2)

funtymeeee
funtymeeee

Reputation: 43

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>

Upvotes: 0

Linus Thiel
Linus Thiel

Reputation: 39261

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

Upvotes: 1

Related Questions