Events
Use events to add interactivity and change state
HTML vs React
In React:
- Events are named using camelCase, rather than lowercase.
- You pass a function as the event handler, rather than a string.
Compare:
- HTML
<button onclick="myFunc()"> Click me </button>
- React JSX - note
onClick
vsonclick
and not callingonClick={ myFunc() }
as that would run on every render rather than on click.<button onClick={ myFunc }> Click me </button>
Or like this:
<button onClick={ () => myFunc(myParam) }> Click me </button>
Basic
Function component
Here we add a click event that logs to the console.
Inline:
function Square{
return (
<button className="square" onClick={ () => console.log('click') }>
{this.props.value}
</button>
);
}
Named function:
function Square{
const handleClick = () => console.log('click')
return (
<button className="square" onClick={ handleClick }>
{this.props.value}
</button>
);
}
Class component
Inline:
class Square extends React.Component {
render() {
return (
<button className="square" onClick={ () => console.log('click') }>
{this.props.value}
</button>
);
}
}
Named function:
class Square extends React.Component {
handleClick() {
console.log('click')
}
render() {
return (
<button className="square" onClick={ this.handleClick }>
{this.props.value}
</button>
);
}
}
Prevent default behavior
Use e.preventDefault()
where e
is a Synthetic event and works cross-browser.
e.g.
function Form() {
function handleSubmit(e) {
e.preventDefault();
console.log('You clicked submit.');
}
return (
<form onSubmit={ handleSubmit }>
<button type="submit">Submit</button>
</form>
);
}
Change state
Use this.setState
to set a value in state on a click:
class Square extends React.Component {
constructor(props) {
super(props);
this.state = {
value: null,
};
}
render() {
return (
<button className="square" onClick={ () => this.setState({ value: 'X' }) }>
{ this.state.value }
</button>
);
}
}
Call a function given as a property
This is useful if you want to change state in a level higher than the current component.
class Square extends React.Component {
render() {
return (
<button className="square" onClick={ () => this.props.onClick() }>
{this.props.value}
</button>
);
}
}
// Usage:
<Square onClick={ myClickFunction } />
Add event listener
When using React, you generally donโt need to call
addEventListener
to add listeners to a DOM element after it is created.Instead, just provide a listener when the element is initially rendered.
Here we add handleClick
as a method attach it to onClick
in the render
method:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = { isToggleOn: true };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
The bind call is necessary to make this
work in the callback.
You have to be careful about the meaning of
this
in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bindthis.handleClick
and pass it toonClick
,this
will beundefined
when the function is actually called.
This applies for JS in general:
Generally, if you refer to a method without
()
after it, such asonClick={this.handleClick}
, you should bind that method.
Alternatively, add an arrow function and leave out bind
.
class Greet extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}
In most cases this is fine. Though, this approach can cause performance issues if a prop is passed to lower components. So the bind
approach is recommended.
Pass arguments to event handlers
You might have a loop where you have an argument to pass to the event call.
Here we pass the id
of a row.
- Using an arrow function and passing the event as
e
explicitly:<button onClick={ (e) => this.deleteRow(id, e) }> Delete Row </button>
- Equivalent syntax using
bind
, where arguments like event are passed implicitly.<button onClick={ this.deleteRow.bind(this, id) }> Delete Row </button>
Form
Using submit and click events:
<form onSubmit={ handleSubmit }>
<input name="postName" onChange={ handleChange } required />
<button type="submit">
Add post
</button>
</form>
Dynamic event names
Capture any event and use it.
const handleChange = (event) => {
setNewPost({
...newPost,
[event.target.name]: event.target.value
})
}