Reputation: 1023
I came of with a solution for pre checked radio button based on JSON data but I think the code can be improved. Or there should be a better way to implement it. The data is structure in following way:
{
radioA: 'Radio A',
radioB: 'Radio B',
radioC: 'Radio C',
selected: 'Radio A'
}
Not sure if this is the best way to do it. The data structure can be modified if needed.
Based on the data provided I am rendering radio button with one of them pre-selected. Following is what I have.
var App = React.createClass({
getInitialState: function(){
return {
radioA: 'Radio A',
radioB: 'Radio B',
radioC: 'Radio C',
selected: 'Radio A'
}
},
render: function(){
return(
<div>
{Object.keys(this.state).map(function(key) {
// Not displaying the selected data
if(key !== 'selected'){
// Checked radio button
if(this.state[key] == this.state['selected']){
return(
<div>
<label className="radio-inline" key={key} htmlFor={key}>
<input id={key} type="radio" checked value={key} /> {this.state[key]}
</label>
</div>
);
}
else{
return(
<div>
<label className="radio-inline" key={key} htmlFor={key}>
<input id={key} type="radio" value={key} /> {this.state[key]}
</label>
</div>
);
}
}
}, this)}
</div>
);
}
});
React.render(<App />, document.getElementById('container'));
The code is set up following jsfiddle.com https://jsfiddle.net/rexonms/2x7ey2L5/
Is there a better solution to implement this? Thanks in advance
Upvotes: 3
Views: 13417
Reputation: 106107
There is indeed a better way. In your render
method, the JSX inside the two branches of your if
statement are identical, except that one has checked
and the other doesn't. Repetition like that can—and should—almost always be cleaned up.
Note: This answer was written with React 0.14 in mind. To see the code as written with more modern React idioms, see this revision.
In the case of boolean attributes like checked
in JSX, you can give them a truthy or falsy value to control whether or not the rendered HTML element has the attribute or not. In other words, if you have <input checked={10 > 1}/>
, you'll get <input checked/>
. If you have <input checked={10 > 100}/>
, you'll get <input/>
.
With that in mind, we can reduce your code to something like this:
var App = React.createClass({
getInitialState: function() {
return {
radios: {
radioA: 'Radio A',
radioB: 'Radio B',
radioC: 'Radio C'
},
selected: 'radioA'
}
},
renderRadioWithLabel: function(key) {
var isChecked = key === this.state.selected;
return (
<label className="radio-inline" key={key} htmlFor={key}>
<input id={key} type="radio" checked={isChecked} value={key} />
{this.state.radios[key]}
</label>
);
},
render: function() {
return (
<div>
{Object.keys(this.state.radios).map(function(key) {
return this.renderRadioWithLabel(key);
}, this)}
</div>
);
}
});
React.render(<App />, document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="container"></div>
As you can see, I've broken the logic that renders the radio buttons and their labels into its own function. You don't have to do that, but I find that it makes render
functions with inner loops a lot easier to read, and makes your intent clearer.
Some people, myself included, would advocate breaking that logic out into its own component so you can have a stateful container component and stateless child components, which makes the whole thing much, much easier to test. Here's a good article on the topic. One more thing you might want to do is make the list of keys and labels in an array, since the order of an object's properties is not guaranteed in JavaScript. If you want to know what it might look like with those changes, take a look at this snippet:
var Radio = React.createClass({
render: function() {
var key = this.props.key;
return (
<label className="radio-inline" key={key} htmlFor={key}>
<input id={key} type="radio" checked={this.props.selected} value={key} />
{this.props.label}
</label>
);
}
});
var RadiosContainer = React.createClass({
getInitialState: function() {
return { selected: this.props.initialSelected };
},
render: function() {
return (
<div>
{this.props.radios.map(function(radioProps) {
var selected = this.state.selected === radioProps.key;
return <Radio {...radioProps} selected={selected} />;
}, this)}
</div>
);
}
});
var radios = [
{ key: 'radioA', label: 'Radio A' },
{ key: 'radioB', label: 'Radio B' },
{ key: 'radioC', label: 'Radio C' }
];
var selectedRadio = 'radioB';
React.render(
<RadiosContainer radios={radios} initialSelected={selectedRadio} />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="container"></div>
Upvotes: 6