Reputation: 12394
Assume I have this component
class Foo extends React.Component {
onClick = () => {
alert('Yay');
this.props.setWhatever()
}
render() {
return (
<Bar>
<input
type='radio'
checked={this.props.getWhatever}
onClick={() => this.onClick()} />
</Bar>
)
}
}
class Bar extends React.Component {
render() {
return (
<label>
{this.props.children}
</label>
)
}
}
The this.onClick()
is not executed. When I change onClick={() => this.onClick()}
to onClick={this.onClick()}
it works, but ends in an endless loop. Why is that?
Upvotes: 1
Views: 98
Reputation: 536
Arrow function expressions are ill suited as methods, and they cannot be used as constructors.
Arrow functions automatically binds this
to the parent/outside components or the context where the function is defined/executed.
onClick={() => this.onClick()}
- when the click happens this
actually refers to the input button and not your component.
onClick={this.onClick()}
- A closure is created and executed as soon as your component is rendered.
onClick={this.onClick}
- A closure is created and executed when click has happened.Please bind all your functions in constructor and avoid arrow functions.
constructor(props){this.onClick = this.onClick.bind(this)}
...
onClick() {...}
...
<input onClick={this.onClick} />
Please read more about arrow functions here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
Update:
Avoid arrow functions in places where you would want to use this
inside that arrow functions.
Upvotes: 1
Reputation: 20755
You should always use onChange
event when working with checked
attribute for radio type input
. In your case you might have encounter with warning.
Warning: Failed prop type: You provided a `checked` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultChecked`. Otherwise, set either `onChange` or `readOnly`.
In your Foo
component do this,
<input
type="radio"
checked={this.props.getWhatever}
onChange={this.onClick.bind(this)} //Bind "this" here or in contructor or use arrow function as you are using
/>
Working Demo
Upvotes: 0
Reputation: 30360
By setting the <input />
element's onClick
prop to onClick={this.onClick()}
, this causes the onClick
function defined in Foo
to be called each time the <input />
element is rendered.
Calling Foo's onClick
will in turn call this.props.setWhatever()
which, as I understand from you question, causes the <Foo/>
component to re-render. Re-rendering the Foo component triggers the cycle to repeat again, which causes the endless loop behavior that you're noticing.
By setting the onClick
prop to onClick={() => this.onClick()}
, you're instead (locally) declaring an arrow function, and passing that to the onClick
prop (rather than calling it immediately as detailed above).
With this "arrow function approach", when the input element is clicked, that event triggers the function that you defined and passed to the onClick
prop to be called as a side effect of that user event.
Hope that clarifies things :-)
There are a few ways to pass extra arguments from an onClick
handler. One simple approach would be:
{/* Inside render() */}
<input type='radio' checked={this.props.getWhatever}
onClick={(event) => this.onClick(event, 'some', 'other', 'arguments')} />
And then update your components onClick
method:
onClick = (event, second, third, fourth) => {
console.log(second, third, fourth); // 'some', 'other', 'arguments'
this.props.setWhatever()
}
Upvotes: 2