We use cookies to enhance your experience on the site
CodeWorlds

Event Handling

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.

Basics of event handling in React

In React, event handling works similarly to standard HTML, but with a few important differences:

  1. Event names use camelCase convention instead of lowercase:

    • HTML:
      onclick
      ,
      onmouseover
    • React:
      onClick
      ,
      onMouseOver
  2. In JSX, we pass a function as the event handler, not a string:

    • HTML:
      <button onclick="handleClick()">Click</button>
    • React:
      <button onClick={handleClick}>Click</button>
  3. To prevent default behavior, we must explicitly call

    preventDefault
    :

    • HTML:
      <a href="#" onclick="return false">Click</a>
    • React:
      <a href="#" onClick={(e) => e.preventDefault()}>Click</a>

Responding to user actions

Events are how React components respond to user actions. Let's look at the basic event types and how to handle them.

Handling clicks

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}

Arguments in event handlers

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}

Passing arguments to event handlers

Sometimes we need to pass additional arguments to an event handler. We can do this in two ways:

1. Using an arrow function

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}

2. Using
bind

1function 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.

Popular events in React

React supports a wide range of events, which can be divided into several categories:

Mouse events

  • onClick
    - click
  • onDoubleClick
    - double click
  • onMouseEnter
    - cursor enters an element
  • onMouseLeave
    - cursor leaves an element
  • onMouseMove
    - cursor moves over an element

Form events

  • onChange
    - change in a form element's value
  • onSubmit
    - form submission
  • onFocus
    - element receives focus
  • onBlur
    - element loses focus

Keyboard events

  • onKeyDown
    - key pressed down
  • onKeyPress
    - key pressed (character)
  • onKeyUp
    - key released

Touch events (for mobile devices)

  • onTouchStart
    - touch begins
  • onTouchMove
    - movement during touch
  • onTouchEnd
    - touch ends

Examples of handling different events

Mouse events with state

1function 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}

Keyboard events

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}

Passing functions as props

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}

Form handling

Forms are a key element of user interaction in web applications. React offers two approaches to form handling: controlled and uncontrolled components.

Controlled forms

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:

  1. Each form field has a corresponding entry in the state
  2. The value of each field is controlled by state
  3. The
    onChange
    event updates the state, which in turn updates the field value
  4. The
    onSubmit
    event is used to handle form submission

Handling multiple form field types

Different 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}

Error handling and form validation

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}

Summary

Event handling is a key element of creating interactive applications in React. Thanks to it, we can:

  1. Respond to user actions such as clicks, mouse movement, or text input
  2. Update component state based on interactions
  3. Communicate between components by passing functions as props
  4. Handle forms and validate user input

Proper event handling, combined with state management, allows you to create dynamic, responsive user interfaces that provide users with an intuitive experience.

Go to CodeWorlds