TLDR: Use defaultChecked instead of checked, working jsbin.
Trying to setup a simple checkbox that will cross out its label text when it is checked. For some reason handleChange is not getting fired when I use the component. Can anyone explain what I'm doing wrong?
var CrossoutCheckbox = React.createClass({
getInitialState: function () {
return {
complete: (!!this.props.complete) || false
};
},
handleChange: function(){
console.log('handleChange', this.refs.complete.checked); // Never gets logged
this.setState({
complete: this.refs.complete.checked
});
},
render: function(){
var labelStyle={
'text-decoration': this.state.complete?'line-through':''
};
return (
<span>
<label style={labelStyle}>
<input
type="checkbox"
checked={this.state.complete}
ref="complete"
onChange={this.handleChange}
/>
{this.props.text}
</label>
</span>
);
}
});
Usage:
React.renderComponent(CrossoutCheckbox({text: "Text Text", complete: false}), mountNode);
Solution:
Using checked doesn't let the underlying value change (apparently) and thus doesn't call the onChange handler. Switching to defaultChecked seems to fix this:
var CrossoutCheckbox = React.createClass({
getInitialState: function () {
return {
complete: (!!this.props.complete) || false
};
},
handleChange: function(){
this.setState({
complete: !this.state.complete
});
},
render: function(){
var labelStyle={
'text-decoration': this.state.complete?'line-through':''
};
return (
<span>
<label style={labelStyle}>
<input
type="checkbox"
defaultChecked={this.state.complete}
ref="complete"
onChange={this.handleChange}
/>
{this.props.text}
</label>
</span>
);
}
});
If you have a handleChange
function that looks like this:
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value,
});
}
You can create a custom onChange
function so that it acts like an text input would:
<input
type="checkbox"
name="check"
checked={this.state.check}
onChange={(e) => {
this.handleChange({
target: {
name: e.target.name,
value: e.target.checked,
},
});
}}
/>
onChange will not call handleChange on mobile when using defaultChecked. As an alternative you can can use onClick and onTouchEnd.
<input onClick={this.handleChange} onTouchEnd={this.handleChange} type="checkbox" defaultChecked={!!this.state.complete} />;
In material ui, state of checkbox can be fetched as
this.refs.complete.state.switched
In case someone is looking for a universal event handler the following code can be used more or less (assuming that name property is set for every input):
this.handleInputChange = (e) => {
item[e.target.name] = e.target.type === "checkbox" ? e.target.checked : e.target.value;
}
In the scenario you would NOT like to use the onChange handler on the input DOM, you can use the onClick
property as an alternative. The defaultChecked
, the condition may leave a fixed state for v16 IINM.
class CrossOutCheckbox extends Component {
constructor(init){
super(init);
this.handleChange = this.handleChange.bind(this);
}
handleChange({target}){
if (target.checked){
target.removeAttribute('checked');
target.parentNode.style.textDecoration = "";
} else {
target.setAttribute('checked', true);
target.parentNode.style.textDecoration = "line-through";
}
}
render(){
return (
<span>
<label style={{textDecoration: this.props.complete?"line-through":""}}>
<input type="checkbox"
onClick={this.handleChange}
defaultChecked={this.props.complete}
/>
</label>
{this.props.text}
</span>
)
}
}
I hope this helps someone in the future.
It's better not to use refs in such cases. Use:
<input
type="checkbox"
checked={this.state.active}
onClick={this.handleClick}
/>
There are some options:
checked
vs defaultChecked
The former would respond to both state changes and clicks. The latter would ignore state changes.
onClick
vs onChange
The former would always trigger on clicks.
The latter would not trigger on clicks if checked
attribute is present on input
element.
Source: Stackoverflow.com