Reputation: 914
I have a component that renders three custom radio buttons. The user can either submit the selected or clear (unselect) them, leaving with no selected radio buttons.
I tried some options with comparing the filterResult
to the data.value
, but without success. Here's a simplified code:
// imports
...
type Props = {
filterConfig: PropTypes.object,
filterValue: Proptypes.string,
onFilterChange: PropTypes.func
}
class Filter extends React.Component {
this.props = Props
this.state = {
filterValue: this.props.filterValue,
}
handleChange = (e) => {
this.setState({ filterValue: e.target.value })
}
handleSubmit = () => {
this.props.onFilterChange(this.state.filterValue)
this.refs.filterContainer.close()
}
handleClear = () => {
this.setState({ filterValue: '' })
}
renderOptions = () => {
const { data, name } = this.props.filterConfig
const options = data.map(
(o, i) => (
<div className='custom-radio' key={i}>
<input
id={`${name}-${i}`}
name={name}
onChange={this.handleChange}
type='radio'
value={o.value}
/>
<label htmlFor={`${name}-${i}`}>
<span />
{o.label}
</label>
</div>
)
)
return (
<div>
{options}
</div>
)
}
renderPickerNavigation = () => {
return (
<div>
<a
href='javascript:void(0)'
onClick={this.handleClear}
>
Clear
</a>
<a
href='javascript:void(0)'
onClick={this.handleSubmit}
>
Done
</a>
</div>
)
}
render = () => {
return (
<FilterWrapper
ref='filterWrapper'
>
{this.renderOptions()}
{this.renderPickerNavigation()}
</FilterWrapper>
)
}
}
The data I'm passing in is:
const filters = [
{
data: [{
label: 'Label 1',
value: 1
}, {
label: 'Label 2',
value: 2
}, {
label: 'Label 3',
value: 3
}],
name: 'userFilter'
}
]
EDIT: The click event on the native radio input works fine, so no need to change that to be on the custom radio (the span
element) or the label.
Upvotes: 1
Views: 6140
Reputation: 59491
You should begin with having a state variable that stores which radio is currently selected. The initial value for this should be null
(or some other falsy value) if you want none to be pre-selected.
The reset button should trigger a function which resets this state variable back to the initial value.
Take a look at this simple demo, using custom css radio buttons as you requested:
class MyApp extends React.Component {
constructor() {
super();
this.state = {
selectedRadio: null,
products: [{id: 1, name: "foo"}, {id: 2, name: "bar"}, {id: 3, name: "baz"}]
}
}
select = (id) => {
this.setState({selectedRadio: id});
}
reset = () => {
this.setState({selectedRadio: null});
}
render() {
return (
<div>
{this.state.products.map(
(item) => {
return (
<div key={item.id}>
<input type="radio" name="myRadio" checked={this.state.selectedRadio === item.id} />
<label onClick={this.select.bind(this, item.id)}>{item.name}<span /></label>
</div>
);
}
)}
<button onClick={this.reset}>Reset</button>
</div>
);
}
}
ReactDOM.render(<MyApp />, document.getElementById("app"));
div {
margin: 10px 0;
}
input[type="radio"] {
display: none;
}
input[type="radio"]+label span {
display: inline-block;
width: 14px;
height: 14px;
margin-left: 4px;
vertical-align: middle;
cursor: pointer;
border-radius: 34%;
}
input[type="radio"]+label span {
background-color: #333;
}
input[type="radio"]:checked+label span {
background-color: orange;
}
<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>
Note: Since you are hiding the input element with css, you cannot have any listeners on it (e.g onChange
or onClick
). Instead, you should have onClick
on the span
that replaces it (see code below).
For a solution of how to reset all "traditional", non-css-only radio buttons, see the snippet below:
class MyApp extends React.Component {
constructor() {
super();
this.state = {
selectedRadio: null,
products: [{id: 1, name: "foo"}, {id: 2, name: "bar"}, {id: 3, name: "baz"}]
}
}
select = (id) => {
this.setState({selectedRadio: id});
}
reset = () => {
this.setState({selectedRadio: null});
}
render() {
return (
<div>
{this.state.products.map(
(item) => {
return (
<div key={item.id}>
<label>{item.name}</label>
<input type="radio" name="myRadio" onChange={this.select.bind(this, item.id)} checked={this.state.selectedRadio === item.id} />
</div>
);
}
)}
<button onClick={this.reset}>Reset</button>
</div>
);
}
}
ReactDOM.render(<MyApp />, 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: 3
Reputation: 1282
This example can help you https://codepen.io/evoyan/pen/vxGBOw
Code:
class Radio extends React.Component {
constructor(props) {
super(props);
this.state = {selected: false};
}
toggle() {
const {onChange} = this.context.radioGroup;
const selected = !this.state.selected;
this.setState({selected});
onChange(selected, this);
}
setSelected(selected) {
this.setState({selected});
}
render() {
let classname = this.state.selected ? 'active' : ''
return (
<button type="button" className={classname} onClick={this.toggle.bind(this)}>
{this.state.selected ? 'yes' : 'no'}
</button>
);
}
}
Radio.contextTypes = {
radioGroup: React.PropTypes.object
};
class RadioGroup extends React.Component {
constructor(props) {
super(props);
this.options = [];
}
getChildContext() {
const {name} = this.props;
return {radioGroup: {
name,
onChange: this.onChange.bind(this)
}};
}
onChange(selected, child) {
this.options.forEach(option => {
if (option !== child) {
option.setSelected(!selected);
}
});
}
render() {
let children = React.Children.map(this.props.children, child => {
return React.cloneElement(child, {
ref: (component => {this.options.push(component);})
});
});
return <div className="radio-group">{children}</div>;
}
}
RadioGroup.childContextTypes = {
radioGroup: React.PropTypes.object
};
class Application extends React.Component {
render() {
return (
<RadioGroup name="test">
<Radio value="1" />
<Radio value="2" />
<Radio value="3" />
</RadioGroup>
);
}
}
/*
* Render the above component into the div#app
*/
ReactDOM.render(<Application />, document.getElementById('app'));
Upvotes: 0