Reputation: 43
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
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
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