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
onClickvsonclickand 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
addEventListenerto 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
thisin JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bindthis.handleClickand pass it toonClick,thiswill beundefinedwhen 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
eexplicitly:<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
})
}