On a cosmic journey through React, event handling is like a flight control system, allowing you to respond to various user actions. It is a key element of creating interactive interfaces that respond to user actions such as clicks, text input, or mouse movements.
In React, event handling works similarly to standard HTML, but with a few important differences:
Event names use camelCase convention instead of lowercase:
onclick, onmouseoveronClick, onMouseOverIn JSX, we pass a function as the event handler, not a string:
<button onclick="handleClick()">Click</button><button onClick={handleClick}>Click</button>To prevent default behavior, we must explicitly call
preventDefault:<a href="#" onclick="return false">Click</a><a href="#" onClick={(e) => e.preventDefault()}>Click</a>Events are how React components respond to user actions. Let's look at the basic event types and how to handle them.
The simplest and most commonly used event is
onClick:1function LaunchButton() {
2 const handleLaunch = () => {
3 console.log('Starting the countdown to launch!');
4 // Rocket launch logic...
5 };
6
7 return (
8 <button
9 className="launch-button"
10 onClick={handleLaunch}
11 >
12 Start Countdown
13 </button>
14 );
15}Every event handler receives an event object as its first argument. It contains information about the event and offers methods like
preventDefault():1function NavigationLink() {
2 const handleClick = (event) => {
3 event.preventDefault(); // Prevents the default action (e.g., navigating to another page)
4 console.log('Link clicked, but the page did not reload');
5
6 // Custom navigation or other logic...
7 };
8
9 return (
10 <a
11 href="/destination"
12 onClick={handleClick}
13 >
14 Go to Destination
15 </a>
16 );
17}Sometimes we need to pass additional arguments to an event handler. We can do this in two ways:
1function MissionControl({ missions }) {
2 const selectMission = (missionId, event) => {
3 console.log(`Selected mission with ID: ${missionId}`);
4 console.log('Event:', event);
5 // Mission selection logic...
6 };
7
8 return (
9 <div className="mission-control">
10 <h2>Available Missions</h2>
11 <ul>
12 {missions.map(mission => (
13 <li key={mission.id}>
14 {mission.name}
15 <button onClick={(e) => selectMission(mission.id, e)}>
16 Select
17 </button>
18 </li>
19 ))}
20 </ul>
21 </div>
22 );
23}bind1function MissionControl({ missions }) {
2 const selectMission = (missionId, event) => {
3 console.log(`Selected mission with ID: ${missionId}`);
4 // Mission selection logic...
5 };
6
7 return (
8 <div className="mission-control">
9 <h2>Available Missions</h2>
10 <ul>
11 {missions.map(mission => (
12 <li key={mission.id}>
13 {mission.name}
14 <button onClick={selectMission.bind(null, mission.id)}>
15 Select
16 </button>
17 </li>
18 ))}
19 </ul>
20 </div>
21 );
22}The arrow function approach is more common and usually more readable.
React supports a wide range of events, which can be divided into several categories:
onClick - clickonDoubleClick - double clickonMouseEnter - cursor enters an elementonMouseLeave - cursor leaves an elementonMouseMove - cursor moves over an elementonChange - change in a form element's valueonSubmit - form submissiononFocus - element receives focusonBlur - element loses focusonKeyDown - key pressed downonKeyPress - key pressed (character)onKeyUp - key releasedonTouchStart - touch beginsonTouchMove - movement during touchonTouchEnd - touch ends1function InteractivePanel() {
2 const [isHovered, setIsHovered] = useState(false);
3 const [isClicked, setIsClicked] = useState(false);
4
5 return (
6 <div
7 className={`panel ${isHovered ? 'hovered' : ''} ${isClicked ? 'clicked' : ''}`}
8 onMouseEnter={() => setIsHovered(true)}
9 onMouseLeave={() => {
10 setIsHovered(false);
11 setIsClicked(false);
12 }}
13 onClick={() => setIsClicked(!isClicked)}
14 >
15 <h3>Interactive Panel</h3>
16 <p>
17 {isHovered ? 'Cursor over panel' : 'Hover over the panel'}
18 {isClicked ? ' - Panel active' : ' - Click to activate'}
19 </p>
20 </div>
21 );
22}1function SpaceshipControls() {
2 const [position, setPosition] = useState({ x: 50, y: 50 });
3
4 const handleKeyDown = (event) => {
5 switch(event.key) {
6 case 'ArrowUp':
7 setPosition(prev => ({ ...prev, y: Math.max(0, prev.y - 5) }));
8 break;
9 case 'ArrowDown':
10 setPosition(prev => ({ ...prev, y: Math.min(100, prev.y + 5) }));
11 break;
12 case 'ArrowLeft':
13 setPosition(prev => ({ ...prev, x: Math.max(0, prev.x - 5) }));
14 break;
15 case 'ArrowRight':
16 setPosition(prev => ({ ...prev, x: Math.min(100, prev.x + 5) }));
17 break;
18 default:
19 return;
20 }
21 };
22
23 // Registering the event at the document level
24 useEffect(() => {
25 document.addEventListener('keydown', handleKeyDown);
26
27 // It is important to clean up listeners when the component unmounts
28 return () => {
29 document.removeEventListener('keydown', handleKeyDown);
30 };
31 }, []);
32
33 return (
34 <div className="spaceship-simulator">
35 <h2>Spaceship Simulator</h2>
36 <p>Use arrow keys to steer the ship</p>
37
38 <div className="universe" style={{ position: 'relative', width: '400px', height: '400px', background: 'black' }}>
39 <div
40 className="spaceship"
41 style={{
42 position: 'absolute',
43 left: `${position.x}%`,
44 top: `${position.y}%`,
45 transform: 'translate(-50%, -50%)',
46 width: '20px',
47 height: '20px',
48 background: 'red'
49 }}
50 ></div>
51 </div>
52
53 <div className="coordinates">
54 <p>Position X: {position.x}</p>
55 <p>Position Y: {position.y}</p>
56 </div>
57 </div>
58 );
59}We often need to pass event handler functions from a parent component to a child component. This is a common practice that enables communication between components:
1function MissionControl({ onLaunch, onAbort }) {
2 const [countdown, setCountdown] = useState(10);
3 const [isLaunching, setIsLaunching] = useState(false);
4
5 const startCountdown = () => {
6 setIsLaunching(true);
7
8 const timer = setInterval(() => {
9 setCountdown(prev => {
10 if (prev <= 1) {
11 clearInterval(timer);
12 onLaunch(); // Calling the function from props after countdown finishes
13 return 0;
14 }
15 return prev - 1;
16 });
17 }, 1000);
18 };
19
20 const abortLaunch = () => {
21 setIsLaunching(false);
22 setCountdown(10);
23 onAbort(); // Calling the function from props on abort
24 };
25
26 return (
27 <div className="mission-control">
28 <h2>Mission Control Center</h2>
29 <div className="countdown">
30 {isLaunching
31 ? `Launch in: ${countdown}`
32 : 'Ready for launch'}
33 </div>
34
35 <div className="control-buttons">
36 {!isLaunching ? (
37 <button onClick={startCountdown}>Start Countdown</button>
38 ) : (
39 <button onClick={abortLaunch} className="abort">Abort Launch</button>
40 )}
41 </div>
42 </div>
43 );
44}
45
46// Using the component
47function SpaceProgram() {
48 const handleLaunch = () => {
49 console.log('Rocket has launched!');
50 // Post-launch logic...
51 };
52
53 const handleAbort = () => {
54 console.log('Launch aborted!');
55 // Post-abort logic...
56 };
57
58 return (
59 <div className="space-program">
60 <h1>Space Program</h1>
61 <MissionControl
62 onLaunch={handleLaunch}
63 onAbort={handleAbort}
64 />
65 </div>
66 );
67}Forms are a key element of user interaction in web applications. React offers two approaches to form handling: controlled and uncontrolled components.
In controlled components, form data is controlled by React state:
1function MissionSetup() {
2 const [missionData, setMissionData] = useState({
3 name: '',
4 destination: 'Moon',
5 crew: 3,
6 duration: 7
7 });
8
9 const handleChange = (e) => {
10 const { name, value } = e.target;
11 setMissionData({
12 ...missionData,
13 [name]: value
14 });
15 };
16
17 const handleSubmit = (e) => {
18 e.preventDefault();
19 console.log('Mission data:', missionData);
20 // Data submission logic...
21 };
22
23 return (
24 <div className="mission-setup">
25 <h2>Mission Configuration</h2>
26
27 <form onSubmit={handleSubmit}>
28 <div className="form-group">
29 <label htmlFor="name">Mission Name:</label>
30 <input
31 type="text"
32 id="name"
33 name="name"
34 value={missionData.name}
35 onChange={handleChange}
36 required
37 />
38 </div>
39
40 <div className="form-group">
41 <label htmlFor="destination">Destination:</label>
42 <select
43 id="destination"
44 name="destination"
45 value={missionData.destination}
46 onChange={handleChange}
47 >
48 <option value="Moon">Moon</option>
49 <option value="Mars">Mars</option>
50 <option value="ISS">International Space Station</option>
51 <option value="Asteroid">Asteroid Belt</option>
52 </select>
53 </div>
54
55 <div className="form-group">
56 <label htmlFor="crew">Number of Crew Members:</label>
57 <input
58 type="number"
59 id="crew"
60 name="crew"
61 min="1"
62 max="10"
63 value={missionData.crew}
64 onChange={handleChange}
65 />
66 </div>
67
68 <div className="form-group">
69 <label htmlFor="duration">Duration (days):</label>
70 <input
71 type="number"
72 id="duration"
73 name="duration"
74 min="1"
75 value={missionData.duration}
76 onChange={handleChange}
77 />
78 </div>
79
80 <button type="submit">Plan Mission</button>
81 </form>
82 </div>
83 );
84}In the example above:
onChange event updates the state, which in turn updates the field valueonSubmit event is used to handle form submissionDifferent form fields require slightly different approaches:
1function AstronautRegistration() {
2 const [formData, setFormData] = useState({
3 name: '',
4 age: '',
5 specialty: 'pilot',
6 experience: '',
7 isTrainee: false,
8 bio: '',
9 preferredShift: 'morning'
10 });
11
12 const handleChange = (e) => {
13 const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
14 setFormData({
15 ...formData,
16 [e.target.name]: value
17 });
18 };
19
20 const handleSubmit = (e) => {
21 e.preventDefault();
22 console.log('Astronaut data:', formData);
23 // Registration logic...
24 };
25
26 return (
27 <div className="astronaut-registration">
28 <h2>Astronaut Registration</h2>
29
30 <form onSubmit={handleSubmit}>
31 {/* Text input */}
32 <div className="form-group">
33 <label htmlFor="name">Full Name:</label>
34 <input
35 type="text"
36 id="name"
37 name="name"
38 value={formData.name}
39 onChange={handleChange}
40 required
41 />
42 </div>
43
44 {/* Number input */}
45 <div className="form-group">
46 <label htmlFor="age">Age:</label>
47 <input
48 type="number"
49 id="age"
50 name="age"
51 value={formData.age}
52 onChange={handleChange}
53 required
54 />
55 </div>
56
57 {/* Select */}
58 <div className="form-group">
59 <label htmlFor="specialty">Specialty:</label>
60 <select
61 id="specialty"
62 name="specialty"
63 value={formData.specialty}
64 onChange={handleChange}
65 >
66 <option value="pilot">Pilot</option>
67 <option value="engineer">Engineer</option>
68 <option value="scientist">Scientist</option>
69 <option value="doctor">Doctor</option>
70 </select>
71 </div>
72
73 {/* Radio buttons */}
74 <div className="form-group">
75 <p>Preferred Shift:</p>
76 <label>
77 <input
78 type="radio"
79 name="preferredShift"
80 value="morning"
81 checked={formData.preferredShift === 'morning'}
82 onChange={handleChange}
83 />
84 Morning
85 </label>
86 <label>
87 <input
88 type="radio"
89 name="preferredShift"
90 value="evening"
91 checked={formData.preferredShift === 'evening'}
92 onChange={handleChange}
93 />
94 Evening
95 </label>
96 </div>
97
98 {/* Checkbox */}
99 <div className="form-group">
100 <label>
101 <input
102 type="checkbox"
103 name="isTrainee"
104 checked={formData.isTrainee}
105 onChange={handleChange}
106 />
107 Trainee Status
108 </label>
109 </div>
110
111 {/* Textarea */}
112 <div className="form-group">
113 <label htmlFor="bio">Biography:</label>
114 <textarea
115 id="bio"
116 name="bio"
117 value={formData.bio}
118 onChange={handleChange}
119 rows="4"
120 ></textarea>
121 </div>
122
123 {/* Range */}
124 <div className="form-group">
125 <label htmlFor="experience">Years of Experience: {formData.experience}</label>
126 <input
127 type="range"
128 id="experience"
129 name="experience"
130 min="0"
131 max="30"
132 value={formData.experience}
133 onChange={handleChange}
134 />
135 </div>
136
137 <button type="submit">Register</button>
138 </form>
139 </div>
140 );
141}In real-world applications, it is important to validate user input:
1function MissionPlanner() {
2 const [formData, setFormData] = useState({
3 missionName: '',
4 launchDate: '',
5 budget: ''
6 });
7
8 const [errors, setErrors] = useState({});
9
10 const validateForm = () => {
11 const newErrors = {};
12
13 // Mission name validation
14 if (!formData.missionName.trim()) {
15 newErrors.missionName = 'Mission name is required';
16 } else if (formData.missionName.length < 3) {
17 newErrors.missionName = 'Mission name must be at least 3 characters';
18 }
19
20 // Launch date validation
21 if (!formData.launchDate) {
22 newErrors.launchDate = 'Launch date is required';
23 } else {
24 const selectedDate = new Date(formData.launchDate);
25 const today = new Date();
26
27 if (selectedDate <= today) {
28 newErrors.launchDate = 'Launch date must be in the future';
29 }
30 }
31
32 // Budget validation
33 if (!formData.budget) {
34 newErrors.budget = 'Budget is required';
35 } else if (isNaN(formData.budget) || Number(formData.budget) <= 0) {
36 newErrors.budget = 'Budget must be a positive number';
37 }
38
39 setErrors(newErrors);
40 return Object.keys(newErrors).length === 0; // Form is valid if there are no errors
41 };
42
43 const handleChange = (e) => {
44 const { name, value } = e.target;
45 setFormData({
46 ...formData,
47 [name]: value
48 });
49
50 // Clear error for the field being changed
51 if (errors[name]) {
52 setErrors({
53 ...errors,
54 [name]: undefined
55 });
56 }
57 };
58
59 const handleSubmit = (e) => {
60 e.preventDefault();
61
62 if (validateForm()) {
63 console.log('Mission data submitted:', formData);
64 // Data submission logic...
65 } else {
66 console.log('Form contains errors');
67 }
68 };
69
70 return (
71 <div className="mission-planner">
72 <h2>Space Mission Planning</h2>
73
74 <form onSubmit={handleSubmit}>
75 <div className="form-group">
76 <label htmlFor="missionName">Mission Name:</label>
77 <input
78 type="text"
79 id="missionName"
80 name="missionName"
81 value={formData.missionName}
82 onChange={handleChange}
83 className={errors.missionName ? 'error' : ''}
84 />
85 {errors.missionName && <div className="error-message">{errors.missionName}</div>}
86 </div>
87
88 <div className="form-group">
89 <label htmlFor="launchDate">Launch Date:</label>
90 <input
91 type="date"
92 id="launchDate"
93 name="launchDate"
94 value={formData.launchDate}
95 onChange={handleChange}
96 className={errors.launchDate ? 'error' : ''}
97 />
98 {errors.launchDate && <div className="error-message">{errors.launchDate}</div>}
99 </div>
100
101 <div className="form-group">
102 <label htmlFor="budget">Budget (in millions $):</label>
103 <input
104 type="number"
105 id="budget"
106 name="budget"
107 value={formData.budget}
108 onChange={handleChange}
109 className={errors.budget ? 'error' : ''}
110 />
111 {errors.budget && <div className="error-message">{errors.budget}</div>}
112 </div>
113
114 <button type="submit">Plan Mission</button>
115 </form>
116 </div>
117 );
118}Event handling is a key element of creating interactive applications in React. Thanks to it, we can:
Proper event handling, combined with state management, allows you to create dynamic, responsive user interfaces that provide users with an intuitive experience.