React: Event Handling

Last updated: 2026-10-26

Event handling is the nerve endings of a React app—when users click buttons, enter text, or submit forms, these interactions are responded to through events. React’s evento system is smarter and more unified than native DOM events.


1. What You'll Learn



2. A Story About Form Interaction

(1) Pain Points: 3 Pitfalls in Form Submission

Bob is building a user registration form and writing evento handlers in native JavaScript:

JAVASCRIPT
// Native JS:Various compatibility issues and details need to be handled manually

// 1. Get DOM Element
const form = document.getElementById('register-form')
const nameInput = document.getElementById('name')
const submitBtn = document.getElementById('submit-btn')

// 2. Event Binding
form.addEventListener('submit', function(event) {
  event.preventDefault()  // Prevent the page from refreshing
  
  // 3. Manually Retrieving Form Data
  const formData = new FormData(form)
  const data = Object.fromEntries(formData)
  
  // 4. Submit Data
  fetch('/api/register', {
    method: 'POST',
    body: JSON.stringify(data)
  })
})

// 5. Real-Time Input Validation
nameInput.addEventListener('input', function(event) {
  if (event.target.value.length < 2) {
    showError('Name (at least) 2 characters')
  } else {
    hideError()
  }
})

// 6. Prevent duplicate submissions when the Submit button is clicked
let isSubmitting = false
submitBtn.addEventListener('click', function() {
  if (isSubmitting) return
  isSubmitting = true
  submitBtn.disabled = true
  submitBtn.textContent = 'Submitting......'
})

Bob's problem:

(2) Event Handling in React

▶ Try it Yourself

Benefits: The code was reduced from 30 lines to 15 lines, with no DOM manipulation, no event unbinding issues, and no browser compatibility issues.



3. React Composite Events

React events are not native DOM events, but rather SyntheticEvents—React listens to them at the top level and simulates a cross-browser event system.

Feature Native DOM Events React Composite Events
Binding Method addEventListener / onclick Write directly in JSX
Browser Compatibility Manual Handling of Differences Required Automatic Standardization
Memory Management Listeners Must Be Removed Manually Automatic Cleanup
Event Object Native Event SyntheticEvent
Prevent Bubbling event.stopPropagation() Identical (Standardized)
Block Default event.preventDefault() Same (Standardized)
100%
graph TB
    A[The user clicks the button] --> B[React Root Node Event Handling]
    B --> C[Create a Synthetic Event Object]
    C --> D[Simulated Bubbles/Capture Phase]
    D --> E[Call JSX Bound to the middle handler]
    
    style B fill:#61dafb,color:#000
    style C fill:#1890ff,color:#fff

Output:

TEXT
...
Clicked the "Log In" button
Button on Mouse Hover
Mouse leaves button


4. Quick Reference for Common Events

Event Name Trigger Conditions Common Scenarios Event Object Type
onClick Element clicked Button, link, card MouseEvent
onChange Changes to input field content Form inputs, drop-down menus ChangeEvent
onSubmit Submit Form Log In/Sign Up/Search FormEvent
onFocus Element gains focus Input field is highlighted FocusEvent
onBlur Element loses focus Input validation FocusEvent
onKeyDown Press a key Shortcut key, Enter to search KeyboardEvent
onKeyUp Press and Release Real-Time Search KeyboardEvent
onMouseEnter Mouseover Hover Effect MouseEvent
onMouseLeave Mouse-out Hover effect MouseEvent
onScroll Scroll Infinite Scroll UIEvent

▶ Example: Comprehensive Overview of Common Events

▶ Expand runnable example (81 lines)
▶ Try it Yourself

Output:

TEXT
Login form with email/password. Invalid email → red border + "The email address format is incorrect." Password <6 → "Password must be at least 6 chars". Submit → console.log({email, password})


5. Event Parameter Passing

(1) Passing Parameters Directly

Wrap it in an arrow function and pass the additional parameters directly:

JSX
function UserList({ users, onDelete }) {
  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>
          {user.name}
          {/* Arrow Functions:Incoming user.id As an additional parameter */}
          <button onClick={() => onDelete(user.id)}>
            Delete
          </button>
        </li>
      ))}
    </ul>
  )
}

// Usage
<UserList
  users={users}
  onDelete={(id) => console.log('Delete User:', id)}
/>
▶ Try it Yourself

(2) Retrieve both the event object and custom parameters

JSX
function ColorPicker({ colors, onSelect }) {
  return (
    <div>
      <p>Select a Color:</p>
      {colors.map(color => (
        <button
          key={color}
          onClick={(event) => {
            // event:Native Synthesis Event Object
            // color:Custom Parameters
            onSelect(color)
            console.log('Click here:', event.clientX, event.clientY)
          }}
          style={{
            backgroundColor: color,
            width: '40px',
            height: '40px',
            border: '2px solid #ddd',
            borderRadius: '50%',
            margin: '4px',
            cursor: 'pointer'
          }}
        />
      ))}
    </div>
  )
}
▶ Try it Yourself

(3) Three Ways to Pass Event Objects

Method Syntax Applicable Scenarios
Implicit Passing onClick={handleClick} No Additional Parameters Required
Arrow Functions onClick={() => handleClick(id)} Requires Parameters
Pass simultaneously onClick={(e) => handleClick(e, id)} Requires an event object + custom parameters


6. Preventing Default Behavior and Bubbling

(1) Prevent the default behavior: preventDefault()

The most common use case is preventing the page from refreshing when a form is submitted:

▶ Try it Yourself

(2) Prevent bubbling: stopPropagation()

▶ Try it Yourself

7. Complete Example: To-Do List (Comprehensive Event Handling)

▶ Expand runnable example (111 lines)
▶ Try it Yourself

Output:

TEXT
... ('Log In:', { email, password })
Clicked the "Log In" button
Button on Mouse Hover
Mouse leaves button

Interaction Flow:

  1. Type the task name in the text box, press Enter, or click the "Add" button → The task is added to the list
  2. Check the checkbox → Mark the task as complete (text is struck through and grayed out)
  3. Click the ✕ button → Delete the task (stopPropagation to prevent double-clicking from triggering the edit function)
  4. Double-click any task → A dialog box for editing appears

▶ Example 2: Keyboard Shortcuts and Focus Management

▶ Expand runnable example (95 lines)
▶ Try it Yourself

Output:

TEXT
Autofocus input. Ctrl+S → "💾 Saved (Ctrl+S)", Ctrl+Z → "↩️ Undo (Ctrl+Z)", Escape → clear, Enter → "✅ Confirm: [text]". Shows pressed keys.

Output:

TEXT
Subheading: "⌨️ Shortcut Keys Panel". Displays: "⌨️ Shortcut Keys Panel". Input: Type here,Try the shortcut keys.... useEffect manages side effects. useRef references DOM/variable

▶ Example 3: Handling Drag-and-Drop Sorting Events

▶ Try it Yourself

Output:

TEXT
4 draggable items (Apple, Banana, Cherry, Date). Drag to reorder; dragged item gets blue highlight border.

Output:

TEXT
Displays: "Drag to Reorder". State: items (setter: setItems), dragIdx (setter: setDragIdx)

▶ Example 4: Custom Hook for Wrapping Event Listeners

▶ Try it Yourself

Output:

TEXT
useEventListener hook: automatically adds/removes DOM event listeners. Cleans up on unmount to prevent memory leaks

Output:

TEXT
State: pos (setter: setPos). useEffect manages side effects. useCallback memoizes handler

▶ Example 5: Form Submission and Event Combination

JSX
function SearchForm({ onSearch }) {
  const [query, setQuery] = useState('')
  const [category, setCategory] = useState('all')

  function handleSubmit(e) {
    e.preventDefault()
    onSearch({ query, category })
  }

  function handleReset() {
    setQuery('')
    setCategory('all')
  }

  return (
    <form onSubmit={handleSubmit} style={{ maxWidth: 400, margin: '0 auto' }}>
      <div style={{ display: 'flex', gap: 8, marginBottom: 8 }}>
        <input value={query} onChange={e => setQuery(e.target.value)}
          placeholder="Search..." style={{ flex: 1, padding: 8, borderRadius: 4 }} />
        <select value={category} onChange={e => setCategory(e.target.value)}
          style={{ padding: 8, borderRadius: 4 }}>
          <option value="all">All</option>
          <option value="electronics">Electronics</option>
          <option value="books">Books</option>
        </select>
      </div>
      <div style={{ display: 'flex', gap: 8 }}>
        <button type="submit" style={{ padding: '6px 16px', cursor: 'pointer' }}>Search</button>
        <button type="button" onClick={handleReset} style={{ padding: '6px 16px', cursor: 'pointer' }}>Reset</button>
      </div>
    </form>
  )
}
▶ Try it Yourself

Output:

TEXT
Search bar + category dropdown (All/Electronics/Books). Search submits {query, category}. Reset clears both fields.

❓ FAQ

Q Why use an arrow function () => handleClick(id) instead of just handleClick(id)?
A If you write onClick={handleClick(id)} directly, React will execute handleClick(id) immediately during rendering, rather than waiting until it’s clicked. This is because it’s a function call (with parentheses), not a function reference (without parentheses). The correct approach is onClick={() => handleClick(id)} (arrow function with delayed execution) or onClick={handleClick} (directly referencing the function name when no arguments are passed).
Q Can you bind multiple identical events to a single element?
A You cannot directly bind two onClick. Solutions: ① Call multiple functions onClick={() => { fn1(); fn2() }} within the same handler; ② Or wrap the element with multiple HOCs. Typically, an element only needs one event handler, within which you can call multiple pieces of logic.
Q What is the difference between React synthetic events and native events?
A React’s SyntheticEvent is a cross-browser wrapper around native events that provides a unified API (such as e.preventDefault() and e.stopPropagation()) and behaves consistently across all browsers. Synthetic events operate under an event delegation mechanism—all events are attached to the root node rather than individual DOM elements. In React 17 and later, events are delegated to the root rather than the document, which prevents conflicts when multiple versions of React coexist.

📖 Summary


📝 Exercises

  1. Basic Exercise (Difficulty ⭐): Create a ButtonCounter component that increments the displayed count by 1 each time the button is clicked. Use the onClick event.
  2. Advanced Exercise (Difficulty ⭐⭐): Create a SearchInput component that implements "debounced search": the search is automatically triggered 500 ms after the user stops typing. Use onChange and useEffect.
  3. Challenge (Difficulty: ⭐⭐⭐): Create a DragAndDropList component that implements drag-and-drop sorting. Use the onDragStart, onDragOver, and onDrop events. List items can be dragged to new positions.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏