Unit -3 Event handling
Event Handling: Basics of Event Handling, Binding Event Handlers, Methods
as Props, Conditional Rendering.
Form handling: Creating a Custom Dynamic Input Component, Setting Up a
JS Config for the Form, Dynamically Create Inputs based on JS Config,
Handling User Input, Handling Form Submission.
In React, event handling is similar to handling events in DOM elements, but
with some syntactic differences:
React events are named using camelCase (onClick instead of onclick)
pass a function as the event handler rather than a string
Event handling in a class component
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
message: 'Hello, welcome to React!',
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
message: 'You clicked the button!',
});
}
render() {
return (
<div>
<h1>{this.state.message}</h1>
{/* Add event handler to the button */}
<button onClick={this.handleClick}>Click Me</button>
</div>
);
}
}
export default App;
Basics
Reading Props in Event Handlers
In React, event handlers often need access to the props passed from parent
components. This is useful when the event handler needs to perform actions
based on the data provided via props.
App.js
import React, { Component } from 'react';
import Child from './Child';
import './App.css';
class Parent extends Component {
render() {
return (
<div className="parent-container">
<h1>Parent Component</h1>
<Child greeting="Hello from Parent!" />
</div>
);
}
}
export default Parent;
Child.js
import React, { Component } from 'react';
class Child extends Component {
handleClick = () => {
alert(this.props.greeting);
};
render() {
return (
<div className="child-container">
<h2>Child Component</h2>
<button onClick={this.handleClick}>Click to See Greeting</button>
</div>
);
}
}
export default Child;
Passing Event Handlers as Props
Event handlers can be passed down to child components as props. This allows
child components to communicate back to the parent and trigger actions in the
parent when events occur.
Parent.js (As a class component)
import React, { Component } from 'react';
import Child from './Child';
class Parent extends Component {
handleClick = () => {
alert("Button clicked in Child component!");
};
render() {
const containerStyle = {
display: "flex",
justifyContent: "center",
alignItems: "flex-start",
height: "100vh",
margin: "0",
};
return (
<div style={containerStyle}>
<div>
<h1>Parent Component</h1>
{/* Passing the event handler as a prop to the Child component */}
<Child onClickHandler={this.handleClick} />
</div>
</div>
);
}
}
export default Parent;
Child.js (As a functional component)
function Child({ onClickHandler }) {
const buttonStyle = {
padding: "12px 24px",
fontSize: "16px",
color: "white",
backgroundColor: "#007bff",
border: "none",
borderRadius: "4px",
cursor: "pointer",
transition: "background-color 0.3s",
};
const buttonHoverStyle = {
backgroundColor: "#0056b3",
};
return (
<div>
<h2>Child Component</h2>
{/* Button to call the event handler */}
<button onClick={onClickHandler}
style={buttonStyle}
onMouseOver={(e)=>(e.target.style.backgroundColor=
buttonHoverStyle.backgroundColor)}
onMouseOut={(e) => (e.target.style.backgroundColor = "#007bff")}
>
Click Me!
</button>
</div>
);
}
export default Child;
Naming Event Handler Props
In React, it’s common to name event handler props according to the event being
handled. This helps in maintaining clarity and consistency in your codebase.
import React from "react";
function Button({ onClickHandler }) {
return <button onClick={onClickHandler}>Click Me</button>;
}
Parent.js
function Parent() {
const handleClick = () => {
alert("Button clicked!");
};
return <Button onClickHandler={handleClick} />;
}
export default Parent;
Need of binding
In React, with out using the bind method , can not refer the event dynamically
gives a reference error.
EventBind.js
import React, { Component } from 'react';
class EventBind extends Component {
constructor(props) {
super(props)
this.state = {
message: 'Welcome'
}
}
clickHandler() {
this.setState({
message:'Farewell'
})
}
render() {
return (
<div>
<h3>{this.state.message}</h3>
<button onClick={this.clickHandler}>Click</button>
</div>
)
}
}
export default EventBind;
The above code will give an error, when the button is clicked, displays a
reference error.
Binding event handlers
Method 1: Using Arrow Functions in Render
Arrow functions can be used directly in the JSX to bind event handlers. This
approach is simple and easy to understand.
App.js
import React, {Component } from 'react';
export default class App extends Component {
handleClick() {
console.log('Button clicked');
}
render() {
return <button onClick={ () => this.handleClick()}> //Arrow function
Click Me
</button>;
}
}
Method 2: Binding in the Constructor
This method involves binding the event handler in the component's constructor.
It's a recommended approach for performance reasons.
App.js
import React, { Component } from 'react';
export default class App extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this); //Binding happens here
}
handleClick() {
console.log('Button clicked');
}
render() {
return <button onClick={this.handleClick}>
Click Me
</button>;
}
}
Method 3: Class Properties as Arrow Functions
Using class fields with arrow functions auto-binds them to the instance of the
class. This is a newer and increasingly popular syntax.
App.js
import React, { Component } from 'react';
export default class App extends Component {
handleClick = () => { //Method is the class property here and passed as
arrow function
console.log('Button clicked');
}
render() {
return <button onClick={this.handleClick}>
Click Me
</button>;
}
}
Passing methods as props
In order to establish the communication from child component to parent
component, passed method as props.
App.js
import React from 'react';
// imports parent component
import ParentComponent from './components/ParentComponent';
function App() {
return (
<div className="App">
<h1>-----------METHODS AS PROPS-------------</h1>
<ParentComponent />
</div>
);
}
export default App;
ParentComponent.js
import React, { Component } from 'react';
//imports child component
import ChildComponent from './ChildComponent';
class ParentComponent extends Component {
constructor(props) {
super(props);
this.state = {
parentName:'Parent'
}
this.greetParent = this.greetParent.bind(this) // binding greet parent method
}
greetParent() {
alert(`Hello ${this.state.parentName}`) //accessing property
}
render() {
return (
<div>
<ChildComponent greetHandler={this.greetParent}/> // child
component renders passed parent component method as props
</div>
)
}
}
export default ParentComponent;
ChildComponent.js
import React from 'react';
function ChildComponent(props) {
return (
<div>
<button onClick={() => props.greetHandler()}> // through the props
being able to communicate with parent component
Greet Parent
</button>
</div>
)
}
export default ChildComponent;
Passing parameters to parents in methods as props
App.js
// App.js
import React from 'react';
// imports component
import ParentComponent from './components/ParentComponent'
function App() {
return (
<div className="App">
<h1>-----------METHODS AS PROPS-------------</h1>
<ParentComponent />
</div>
);
}
export default App;
// ParentComponent.js
import React, { Component } from 'react';
import ChildComponent from './ChildComponent';
class ParentComponent extends Component {
constructor(props) {
super(props);
this.greetParent = this.greetParent.bind(this)
}
greetParent(name) { //Passing parameters to method
alert(`Hello ${name}`)
}
render() {
return (
<div>
<ChildComponent greetHandler={this.greetParent}/>
</div>
)
}
}
export default ParentComponent;
// ChildComponent.js
import React from 'react';
function ChildComponent(props) {
return (
<div>
Greet Parent from child
</button>
</div>
)
}
export default ChildComponent;
Conditional rendering allows dynamic control over which UI elements or
content are displayed based on specific conditions. It is commonly used in
programming to show or hide elements depending on user input, data states, or
system status. This technique improves user experience by ensuring that only
relevant information is presented at a given time.
What is Conditional Rendering in React?
Conditional rendering in React works similarly to conditions in JavaScript. It
enables components to display different outputs depending on states or props.
This ensures that the UI updates dynamically based on logic instead of manually
manipulating the DOM.
Ways to Implement Conditional Rendering in React
1. Using If/Else Statements
If/else statements allow rendering different components based on conditions.
This approach is useful for complex conditions.
function Item({ name, isPacked }) {
if (isPacked) {
return <li className="item">{name} </li>;
}
return <li className="item">{name}</li>;
}
2. Using Ternary Operator
The ternary operator (condition ? expr1: expr2) is a concise way to
conditionally render JSX elements. It’s often used when the logic is simple and
there are only two options to render.
function Greeting({ isLoggedIn }) {
return <h1>{isLoggedIn ? "Welcome Back!" : "Please Sign In"}</h1>;
}
If isLoggedIn is true: Welcome Back!
If isLoggedIn is false: Please Sign In
3. Using Logical AND (&&) Operator
The && operator returns the second operand if the first is true, and nothing if
the first is false. This can be useful when you only want to render something
when a condition is true.
function Notification({ hasNotifications }) {
return <div>{hasNotifications && <p>You have new
notifications!</p>}</div>;
}
If hasNotifications is true: You have new notifications!
If hasNotifications is false: Nothing is rendered.
4. Using Switch Case Statements
Switch case statements are useful when you need to handle multiple conditions,
which would otherwise require multiple if conditions. This approach can be
more readable if there are many conditions to check.
function StatusMessage({ status }) {
switch (status) {
case 'loading':
return <p>Loading...</p>;
case 'success':
return <p>Data loaded successfully!</p>;
case 'error':
return <p>Error loading data.</p>;
default:
return <p>Unknown status</p>;
}
}
If status is ‘loading’: Loading…
If status is ‘success’: Data loaded successfully!
If status is ‘error’: Error loading data.
5. Conditional Rendering in Lists (Using .map())
Conditional rendering can be helpful when rendering lists of items
conditionally. You can filter or map over an array to selectively render
components based on a condition.
const items = ["Apple", "Banana", "Cherry"];
const fruitList = items.map((item, index) =>
item.includes("a") ? <p key={index}>{item}</p> : null
);
If the item contains letter “a”, it will be displayed.
6. Conditional Rendering with Component State
We can also manage conditional rendering based on the component’s state. For
example, you can show a loading spinner until some data is fetched, and then
display the actual content once the data is ready.
Syntax:
if (loading) {
// Render Loading Component
} else {
// Render Data Component
}
App.js
import { useState } from "react";
export default function App() {
const [showText, setShowText] = useState(true);
const textVisibility = () => {
setShowText(!showText);
};
return (
<div className="App">
<center>
{!showText && (
<p>
React JS for Web application development
</p>
)}
<button onClick={textVisibility}>
{showText ? "Show some text" : "Hide it"}
</button>
</center>
</div>
);
}
Form handling
Creating a Custom Dynamic Input Component
Reusable component that renders different input types based on configuration.
function DynamicInput({ type, name, label, options, value, onChange }) {
switch (type) {
case 'text':
case 'email':
case 'password':
return (
<div className="form-group">
<label>{label}</label>
<input
type={type}
name={name}
value={value}
onChange={onChange}
/>
</div>
);
case 'select':
return (
<div className="form-group">
<label>{label}</label>
<select name={name} value={value} onChange={onChange}>
{options.map(opt => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
);
case 'checkbox':
return (
<div className="form-group">
<label>
<input
type="checkbox"
name={name}
checked={value}
onChange={onChange}
/>
{label}
</label>
</div>
);
default:
return null;
}
}
Explanation of the above code
The DynamicInput component renders different input types based on
the type prop.
It handles common input types: text, email, password, select, and
checkbox.
The onChange callback is called with the field name and value for parent
component handling.
Setting Up a JS Config for the Form
Centralized configuration that defines all form fields and their properties.
const formConfig = [
{
type: 'text',
name: 'username',
label: 'Username',
required: true,
validation: value => value.length >= 3
},
{
type: 'email',
name: 'email',
label: 'Email Address',
required: true,
validation: value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
},
{
type: 'select',
name: 'country',
label: 'Country',
options: [
{ value: 'us', label: 'United States' },
{ value: 'ca', label: 'Canada' },
{ value: 'uk', label: 'United Kingdom' }
]
},
{
type: 'checkbox',
name: 'subscribe',
label: 'Subscribe to newsletter',
defaultValue: false
}
];
Explanation:
formConfig defines the structure and validation rules for the form.
initialValues provides default values for each field.
Each field in the fields array specifies:
o type: The input type to render
o name: The field name (must match initialValues keys)
o label: Display label for the field
o validation: Validation rules (optional)
o options: For select inputs (optional)
Dynamically Creating Inputs Based on JS Config
Render form fields automatically from the configuration.
function DynamicForm() {
const [formData, setFormData] = useState({});
const [errors, setErrors] = useState({});
// Initialize form data with default values
useEffect(() => {
const initialData = {};
formConfig.forEach(field => {
initialData[field.name] = field.defaultValue || '';
});
setFormData(initialData);
}, []);
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value
}));
};
return (
<form>
{formConfig.map(field => (
<DynamicInput
key={field.name}
{...field}
value={formData[field.name] || ''}
onChange={handleChange}
/>
))}
</form>
);
Explanation:
The DynamicForm component uses the formConfig to render all form
fields.
formData state holds the current values of all form fields.
Errors state tracks validation errors for each field.
handleChange updates form data and performs validation when fields
change.
Fields are rendered by mapping through the formConfig.fields array.
Handling User Input
Manage form state and validation as users interact with inputs.
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
// Update form data
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value
}));
// Validate if field has validation rules
const fieldConfig = formConfig.find(f => f.name === name);
if (fieldConfig?.validation) {
const isValid = fieldConfig.validation(
type === 'checkbox' ? checked : value
);
setErrors(prev => ({
...prev,
[name]: isValid ? null : `Invalid ${fieldConfig.label}`
}));
}
};
The input handling is already implemented in the DynamicForm component
above through:
The handleChange function that updates form state
The fieldConfig function that performs validation
Passing these handlers down to each DynamicInput component
Handling Form Submission
Process the form data when submitted.
const handleSubmit = (e) => {
e.preventDefault();
// Validate all fields
const newErrors = {};
let isValid = true;
formConfig.forEach(field => {
if (field.required && !formData[field.name]) {
newErrors[field.name] = `${field.label} is required`;
isValid = false;
} else if (field.validation && !field.validation(formData[field.name])) {
newErrors[field.name] = `Invalid ${field.label}`;
isValid = false;
}
});
setErrors(newErrors);
if (isValid) {
// Submit data to API or parent component
console.log('Form submitted:', formData);
// api.submit(formData);
// Optional: Reset form after submission
const resetData = {};
formConfig.forEach(field => {
resetData[field.name] = field.defaultValue || '';
});
setFormData(resetData);
}
};
// Add to form element
<form onSubmit={handleSubmit}>
{/* ... dynamic inputs ... */}
<button type="submit">Submit</button>
</form>
Explanation:
Added handleSubmit function that:
o Prevents default form submission
o Validates the entire form
o Handles the submission if valid or shows errors if invalid
Wrapped the form elements in a <form> tag with onSubmit handler
Added a submit button