Handling events in JSX is pretty similar to handling events in actual DOM.For example if we have a button element and we want to pass a event handler for click event we can pass a props called onClick to the button element.

  1. function ClickableButton(props){
  2. //callback function that's called when button is clicked
  3. const handleClick = () => { alert('Clicked') }
  4. return (
  5. <button onClick={handleClick}></button>
  6. )
  7. }

Notice there are couple difference between how events are handled in HTML vs JSX.

  • With HTML we would have an attribute called onclick (all lowercased), but with JSX have a camelCased props called onClick. In JSX always use camelCase instead of lowercase like in HTML.Ex:
  • onclick => onClick
  • onmouseover => onMouseOver
  • onselect => onSelect
  • onchange => onChange
  • With HTML we would pass a string as a value of the attribute but in JSX we passed actual function. For example compare the above button in JSX to the following HTML equivalent:
  1. <button onclick="handleClick()"></button>

Synthetic events

When React invokes the event-handler, it provides a SyntheticEvent as an argument. Not to worry about any of the details, it's a wrapper around browser's native event and it has the same interface as the native event.

  1. function InputComponent(props){
  2. //callback function that's called when input changes
  3. //It gets synthetic event as an argument
  4. //SyntheticEvent has interface just like the native browser event
  5. //In this case - to get the value of the input we did e.target.value
  6. const handleChange = (e) => { alert(e.target.value) }
  7. return (
  8. <input onChange={handleChange} />
  9. )
  10. }

Function Binding

One gotcha when using event handlers with components created as ES6 classes (by extending React.Component) is the method binding. This has nothing to do with React or JSX but it's new Javascript feature in ES6 and trips even many experienced folks.

Class function in ES6 are not bound to anything by default. For example take below example of Input component. Here we just have an input element whose value is assigned to this.state.inputValue and we are handling onChange event on this input by passing this.handleChange function. Inside the handleChange function we are calling setState with the new value typed by the user. Now if we run this code it will error out saying something like "Cannot read property setState of undefined". Weird huh? The reason is because handleChange is not bound to anything. So this inside handleChange is undefined.

  1. class Input extends React.Component {
  2.  
  3. handleChange(e){
  4. this.setState({
  5. inputValue: e.target.value
  6. });
  7. }
  8.  
  9. render(){
  10. return(
  11. <input onChange={this.handleChange} value={this.state.inputValue}/>
  12. )
  13. }
  14. }

To fix above issue we just need to bind the function to proper this. The common practice is to do that in the constructor. So below we added a line in a constructor this.handleChange = this.handleChange.bind(this) to bind the handleChange function and it would work like a charm.

  1. class Input extends React.Component {
  2. constructor(props){
  3. super(props);
  4.  
  5. //bind handleChange function to proper this
  6. this.handleChange = this.handleChange.bind(this);
  7. }
  8.  
  9. handleChange(e){
  10. this.setState({
  11. inputValue: e.target.value
  12. });
  13. }
  14.  
  15. render(){
  16. return(
  17. <input onChange={this.handleChange} value={this.state.inputValue}/>
  18. )
  19. }
  20. }

Let's write some code. Please open the exercise file and follow the instruction on the file.

Please read this article to get more idea on the ES6 binding dilemma.