Explore how user interactions work in React using events.
Class component methods lose this when passed as DOM callbacks. Here are all the approaches.
No binding
this is undefined inside the handler when invoked by the DOM event system in strict mode.
class Counter extends Component {
handleClick() {
// this = undefined β
this.setState(...)
}
render() {
return (
<button onClick={this.handleClick}>
+
</button>
)
}
}Bind in constructor
Bound once at mount. No extra function created per render. Explicit and classic.
constructor(props) {
super(props)
this.state = { count: 0 }
this.handleClick =
this.handleClick.bind(this) // β
}
handleClick() {
this.setState(s =>
({ count: s.count + 1 })
)
}Class field arrow
Arrow functions capture this lexically. No constructor or explicit bind needed.
class Counter extends Component {
state = { count: 0 }
// Arrow captures this β
handleClick = () => {
this.setState(s =>
({ count: s.count + 1 })
)
}
}Function component + hooks
No this, no binding. Closures capture state directly. The modern default.
function Counter() {
const [count, setCount] =
useState(0)
const handleClick = () => {
setCount(c => c + 1) // β
}
return (
<button onClick={handleClick}>
+
</button>
)
}Events travel down (capture) then up (bubble). Select a mode to see each control method live.
Live demo
Outer div
Middle div
Click the button. The event fires at the target then bubbles up: Inner β Middle β Outer.
Event log
Interact with the demo above...
Code
<div onClick={() => log('Outer')}>
<div onClick={() => log('Middle')}>
<button
onClick={() => log('Inner')}
>
Click
</button>
</div>
</div>
// Fires: Inner β Middle β Outer