How to Set an Event Handler in a React Sub-component
Aug 21, 2020 • 6 Minute Read
Introduction Using React subcomponents is a practice of using multiple components in a component to break down a complex hierarchy into small, independent, and reusable components. These subcomponents can be configured to handle events and data via props. “Props” stands for properties, and are a React feature that passes data between components as an immutable key-value pair object.
Implementing subcomponents is quite easy, but sometimes it can be tricky to retain the context with the use of anonymous functions or React classes. This guide covers the implementation of subcomponents with anonymous and ES6 features in React.
Implementing Events in a React Component
A React class component can be created by extending the Component class, and an onClick event can be implemented with a function to handle the click:
import React, { Component } from 'react';
export default class App extends Component {
handleClick() {
console.log("click");
}
render() {
return (
<div className="container">
<button onClick={this.handleClick} > click </button>
</div>
);
}
}
In the above snippet, the handleClick function will be triggered whenever the button is clicked. There is no need to bind handleClick with this because handleClick is not accessing anything from its container (App) component. Otherwise, there would have been an error, because this points to the window object at runtime, not App.
Passing Event Handler to Subcomponent
A subcomponent can be implemented either as a class or a functional component. The implementation can be optimized using functional components. The Address subcomponent will receive the details via props from the container component:
function Address({ type, houseNo, clickHandler }) {
const isPermanent = type && type === 'Temporary';
return (
<div>
<b>{type}</b>
<p>{houseNo}
{' '}
{isPermanent ? <button onClick={clickHandler} title='Delete'>X</button> : ''}
</p>
</div>
)
}
The Address component is using the destructuring assignment { type, houseNo, clickHandler } to access the passed prop values directly. The clickHandler is a function to handle clicks, which is being passed to the onClick attribute.
The App component is using state to store the data of addresses, and the removeTempAddress function will reset the value of the addresses object with only the permanent object:
export default class App extends Component {
// set the values for the addresses in state object
state = {
addresses: {
permanent: {
houseNo: 'ABC',
},
temporary: {
houseNo: 'XYZ',
}
}
}
// an arrow function to retails the context of this
removeTempAddress = () => {
const { permanent } = this.state.addresses;
// reset value of 'addresses' to 'permanent' only
this.setState({ addresses: { permanent } })
}
render() {
const { permanent, temporary } = this.state.addresses;
return (
<div className="container">
<Address type='Permanent' houseNo={permanent.houseNo} />
{temporary && <Address type='Temporary' houseNo={temporary.houseNo} clickHandler={this.removeTempAddress} />}
</div>
);
}
}
Passing event-handlers via props gives control to the container component to handle the events and helps to create reusable components.
Implementing Subcomponents at Runtime
Generating subcomponents at runtime is a common requirement for dynamic UI. Often the UI has to render/update as per the changes in the list of data items, for example, displaying items in a cart, tabular data for stocks, etc. Due to the dynamic size of the data, the solution is to iterate through data while generating the items. Using the array map is a common method to iterate data values for processing. It takes an anonymous function to process the elements of an array, and an arrow function can be used to access the context (App component) inside an anonymous function:
export default class App extends Component {
state = {
items: ["Banana", "Mango"],
};
handleClick(event) {
console.log(event.target.innerHTML);
}
render() {
var listItems = this.state.items.map((item, index) => {
return <li key={index} onClick={this.handleClick}><a href='/#'>{item}</a></li>;
});
return (
<div className="container">
<ul>{listItems}</ul>
</div>
);
}
}
The use of the arrow function provides access to this.handleClick by preserving the context. Alternately, the map function also takes this as an argument to preserve context:
var listItems = this.state.items.map(function (item, index) {
return <li key={index} onClick={this.handleClick}><a href='/#'>{item}</a></li>;
}, this); // this as second argument to map function
This can also be done using bind:
var listItems = this.state.items.map(function (item, index) {
return <li key={index} onClick={this.handleClick}><a href='/#'>{item}</a></li>;
}.bind(this)); // binding this with anonymous function
Conclusion
Subcomponents are a great way to create reusable elements and to pass events and data via props. At runtime, by default this refers to the window object, and there are many ways to preserve the context in dynamic subcomponents via an arrow function, overloaded methods to pass this, and the bind method. Hopefully, this guide explained the necessary details to create subcomponents in different ways. Happy coding!