React element event handling is quite similar to that of DOM elements, but there is a slight difference in syntax.
React event naming uses camelCase instead of all lowercase.
When using JSX syntax, you need to pass a function as the event handler instead of a string.
1. Event Handling#
1. Event Binding#
Event handling for React elements is similar to that of DOM elements, but there are some syntactical differences, for example:
Traditional HTML: Wrapped in double quotes, must be followed by parameters.
<button onclick="myfun()">Click</button>
React: Wrapped in curly braces, not followed by parameters.
<button onclick={myfun}>Click</button>
A complete event function code is as follows:
class Demo extends React.Component {
render() {
// Event function
function myfun() {
alert('hello, world');
}
return (
// Bind event
<button onClick={this.myfun}>
Activate Lasers
</button>
);
}
}
ReactDOM.render(
<Demo />,
document.getElementById('root')
);
If the method does not have
()
, you need to bindthis
to that method.
2. Preventing Default Behavior#
Another difference in React is that you cannot prevent default behavior by returning false. React provides a property — preventDefault
, which can be used to prevent script execution.
Let's look at the comparison between traditional HTML and React.
<a href="#" onclick="alert('Do you want to pop up?');return false">
Click me
</a>
Simply writing false can prevent script execution.
React prevents script execution through the preventDefault property:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
2. Conditional Rendering#
In React, you can create different components to encapsulate various behaviors you need, and then, based on the different states of the application, you can render only the content corresponding to that state.
Conditional rendering in React is similar to that in JavaScript, using the if
operator to indicate the current state of the element, and then allowing React to update the UI based on them.
Using if..else Statements for Conditional Rendering#
First, let's write an example of conditional rendering, defining two components, and then determining whether to render the UserGreeting
or GuestGreeting
component based on the truthiness of the variable isLoggedIn
in the Greeting
component.
// App.js
import React, { Component } from 'react';
export default class App extends Component {
render() {
function UserGreeting(props) {
return <h3>Welcome back!</h3>;
}
function GuestGreeting(props) {
return <h3>Please sign up.</h3>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
return (
<div>
<Greeting isLoggedIn={false} />
</div>
);
}
}
Finally, the variable isLoggedIn
is defined as false
, so the browser renders GuestGreeting
.
How to Prevent Conditional Rendering?#
In some cases, we may want to hide a component even if it has already been rendered by other components. We can return null
from the render
method to prevent the component from rendering.
In the following example, the <WarningBanner />
will conditionally render based on the value of warn
in the props. If the value of warn
is false
, then the component will not render:
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
Warning!
</div>
);
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {showWarning: true};
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState(state => ({
showWarning: !state.showWarning
}));
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} />
<button onClick={this.handleToggleClick}>
{this.state.showWarning ? 'Hide' : 'Show'}
</button>
</div>
);
}
}
ReactDOM.render(
<Page />,
document.getElementById('root')
);
3. Rendering Lists#
First, let's look at a piece of code where we use the map()
function to double each item in the array and then print out a new array doubled
.
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);
// [2, 4, 6, 8, 10]
In React, the process of converting an array into a list of elements is similar.
First, we use the map()
method to iterate over the numbers
array, transforming each element into an <li>
tag, and finally assigning the resulting array to listItems
.
Then we return {listItems}
.
// Map.js
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);
After running, the browser displays an unordered list of 1-5.
1. Separating Components#
The above is a basic example of list rendering, but the data is hardcoded. Next, we will refactor the array into a component so that we can easily call it for array rendering in the future.
// Map.js
export default class Map extends Component {
render() {
// Separate the component NumberList as the component for transforming the array
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
// Pass in the required data
const numbers = [1, 2, 3, 4, 5, 6, 7];
return (
<div>
<NumberList numbers={numbers} />
</div>
);
}
}
2. Key#
After running the code, the page displays normally, but the console will report an error: Each child in a list should have a unique "key" prop.
, meaning that when you create an element, you must include a special key
property.
Now assign a key to each list element:
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
3. Using ID as Key#
The key
helps React identify which elements have changed, such as being deleted or added, so each element should have a unique identifier, which is the key
.
The key
of an element is best as a unique string that the element has in the list. Typically, we use the id
from the data as the element's key
:
// Map.js
export default class Map extends Component {
render() {
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.id}> // Assign key
{number.text}
</li>
);
return (
<ul>{listItems}</ul>
);
}
// Pass in data
const numbers = [
{id: 1, text: 1},
{id: 2, text: 2},
{id: 3, text: 3},
{id: 4, text: 4},
{id: 5, text: 5}
];
return (
<Fragment>
<NumberList numbers={numbers} />
</Fragment>
);
}
}
4. Can Index Be Used as Key?#
When elements do not have a defined id, you can reluctantly use the element's index as the key
:
const todoItems = todos.map((todo, index) =>
// Only use index as key when there is no defined id
<li key={index}>
{todo.text}
</li>
);
If the order of list items may change, we do not recommend using the index as the key
value, as this can lead to performance degradation and may cause issues with component state.
5. Extracting Components with Key#
For example, if you extract a ListItem
component, you should keep the key
on the <ListItem />
element in the array, rather than placing it on the <li>
element inside the ListItem
component.
Incorrect Usage:
function ListItem(props) {
const value = props.value;
return (
// Incorrect! You don't need to specify key here:
<li key={value.toString()}>
{value}
</li>
);
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Incorrect! The key for the element should be specified here:
<ListItem value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
Correct Usage:
function ListItem(props) {
// Correct! No need to specify key here:
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Correct! Key should be specified in the context of the array
<ListItem key={number.toString()} value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
React: A good rule of thumb is that elements in the map() method need to set the key property.
6. Key Must Be Unique Among Sibling Nodes#
The key
used in array elements should be unique among its sibling nodes. However, they do not need to be globally unique. When we generate two different arrays, we can use the same key
values:
function Blog(props) {
const sidebar = (
<ul>
{props.posts.map((post) =>
<li key={post.id}>
{post.title}
</li>
)}
</ul>
);
const content = props.posts.map((post) =>
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
);
return (
<div>
{sidebar}
<hr />
{content}
</div>
);
}
const posts = [
{id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
{id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(
<Blog posts={posts} />,
document.getElementById('root')
);
7. Rendering Lists in Vue#
In Vue, rendering lists uses the special directive v-for
, which also has related usage for key
.
In React, we use the map()
method to iterate over the array and then render the list.