Guide: Integrating React ⚛️ into a Rails 8 Application – Part 4 | Synthetic Event System | Event Methods | Props

This is about React’s Synthetic Event System and naming conventions. Let me explain why React uses onClick instead of click.

React’s Synthetic Event System

React doesn’t use native DOM events directly. Instead, it creates its own Synthetic Events system that wraps around native DOM events.

HTML vs React Comparison:

<!-- Regular HTML -->
<button onclick="myFunction()">Click me</button>

<!-- React JSX -->
<button onClick={myFunction}>Click me</button>

Key Differences:

1. Naming Convention – camelCase vs lowercase:

// ❌ HTML style (lowercase) - doesn't work in React
<button click={addTodo}>Add Todo</button>
<input change={handleChange} />
<form submit={handleSubmit}>

// ✅ React style (camelCase) - correct
<button onClick={addTodo}>Add Todo</button>
<input onChange={handleChange} />
<form onSubmit={handleSubmit}>

2. More Event Examples:

// HTML → React
onclick    → onClick
onchange   → onChange
onsubmit   → onSubmit
onkeydown  → onKeyDown
onkeyup    → onKeyUp
onmouseenter → onMouseEnter
onmouseleave → onMouseLeave
onfocus    → onFocus
onblur     → onBlur

❓ Why React Uses Synthetic Events?

1. Cross-Browser Compatibility:

// Without React - you'd need to handle browser differences
function addEventListenerCrossBrowser(element, event, handler) {
  if (element.addEventListener) {
    element.addEventListener(event, handler); // Modern browsers
  } else if (element.attachEvent) {
    element.attachEvent('on' + event, handler); // Old IE
  }
}

// With React - it handles all browser differences for you
<button onClick={addTodo}>Add Todo</button>

2. Consistent API:

// React events work the same across all browsers
const handleClick = (e) => {
  e.preventDefault();    // Always works
  e.stopPropagation();   // Always works
  console.log(e.target); // Always works
};

3. Performance Optimization:

// React uses event delegation - attaches one listener to document
// and manages all events from there (more efficient)
<div>
  <button onClick={handleClick1}>Button 1</button>
  <button onClick={handleClick2}>Button 2</button>
  <button onClick={handleClick3}>Button 3</button>
  {/* React doesn't attach 3 separate listeners */}
</div>

What happens behind the scenes:

When you write:
<button onClick={addTodo}>Add Todo</button>
React internally:
  1. Wraps the native click event in a SyntheticEvent
  2. Normalizes the event across different browsers
  3. Delegates the event handling to the document root
  4. Calls your addTodo function with the synthetic event

Example of React’s event object:

const handleClick = (e) => {
  console.log(e.type);        // "click"
  console.log(e.target);      // The button element
  console.log(e.currentTarget); // The button element
  console.log(e.preventDefault); // Function to prevent default

  // React provides the native event if needed
  console.log(e.nativeEvent); // Original DOM event
};

💡 Common Mistakes:

❌ Don’t do this:
// Wrong - lowercase (HTML style)
<button onclick={addTodo}>Add Todo</button>

// Wrong - calling the function immediately
<button onClick={addTodo()}>Add Todo</button>

// Wrong - using string (HTML style)
<button onClick="addTodo()">Add Todo</button>
✅ Do this:
// Correct - camelCase and function reference
<button onClick={addTodo}>Add Todo</button>

// Correct - arrow function for parameters
<button onClick={() => deleteTodo(id)}>Delete</button>

// Correct - arrow function for event access
<button onClick={(e) => handleClick(e, id)}>Click</button>

Full Event System Examples:

const MyComponent = () => {
  const handleClick = (e) => {
    console.log('Button clicked!');
    e.preventDefault(); // Prevent default behavior
  };

  const handleKeyPress = (e) => {
    if (e.key === 'Enter') {
      console.log('Enter pressed!');
    }
  };

  const handleMouseEnter = () => {
    console.log('Mouse entered!');
  };

  return (
    <div>
      <button onClick={handleClick}>Click me</button>
      <input onKeyPress={handleKeyPress} />
      <div onMouseEnter={handleMouseEnter}>Hover me</div>
    </div>
  );
};

📝 Summary:

React uses onClick (camelCase) instead of click (lowercase) because:

  1. Consistency – All React props use camelCase
  2. Synthetic Events – React wraps native events for better cross-browser support
  3. Performance – React optimizes event handling with event delegation
  4. Predictability – Same API across all browsers and components

The onClick you use in React is actually more powerful and reliable than the native onclick in HTML!

Questions

1. How Event Delegation Improves Performance?

Event delegation is a powerful optimization technique. Let me show you the difference:

Without Event Delegation (Traditional DOM):

// Imagine you have 1000 buttons - traditional approach
const buttons = document.querySelectorAll('button');
buttons.forEach(button => {
  button.addEventListener('click', handleClick); // 1000 event listeners!
});

With Event Delegation (React’s approach):

// React attaches ONE listener to the document root
document.addEventListener('click', (e) => {
  if (e.target.matches('button')) {
    handleClick(e); // Only one listener for all buttons!
  }
});

Performance Benefits:

Memory Usage:

// Without delegation: 1000 buttons = 1000 event listeners in memory
// With delegation: 1000 buttons = 1 event listener in memory

DOM Manipulation:

// Without delegation: Adding/removing elements requires managing listeners
const newButton = document.createElement('button');
newButton.addEventListener('click', handleClick); // Must remember to add
document.body.appendChild(newButton);

// With delegation: New elements automatically work
const newButton = document.createElement('button');
document.body.appendChild(newButton); // Automatically handles clicks!

Real React Example:

// Even with 1000 todos, React only has ONE click listener
<ul>
  {todos.map(todo => (
    <li key={todo.id}>
      <button onClick={() => deleteTodo(todo.id)}>Delete</button>
      <button onClick={() => editTodo(todo.id)}>Edit</button>
    </li>
  ))}
</ul>
// React manages all these clicks with ONE delegated listener

2. Event Parameter Naming – why always use ‘e’ for events?

We can absolutely use different names! e is just a convention, not a requirement.

All of these are valid:

// Common conventions
<button onClick={(e) => handleClick(e, id)}>Click</button>
<button onClick={(event) => handleClick(event, id)}>Click</button>
<button onClick={(evt) => handleClick(evt, id)}>Click</button>

// Your custom names
<button onClick={(v) => handleClick(v, id)}>Click</button>
<button onClick={(ev) => handleClick(ev, id)}>Click</button>
<button onClick={(clickEvent) => handleClick(clickEvent, id)}>Click</button>

Why developers use ‘e’:

// 'e' is short and widely recognized
const handleClick = (e) => {
  console.log(e.target);
};

// 'event' is more descriptive but longer
const handleClick = (event) => {
  console.log(event.target);
};

// Custom names work but might confuse other developers
const handleClick = (banana) => {
  console.log(banana.target); // Works but confusing!
};

Best Practice:

// Use 'e' for short inline handlers
<button onClick={(e) => e.preventDefault()}>Click</button>

// Use 'event' for longer, more complex handlers
const handleSubmit = (event) => {
  event.preventDefault();
  event.stopPropagation();
  // ... more logic
};

3. Event Methods and Properties Explained

Explain preventDefault, stopPropagation, currentTarget, target, type etc.

Let me create a comprehensive example:

const EventDemo = () => {
  const handleClick = (e) => {
    console.log('=== EVENT PROPERTIES ===');
    console.log('e.type:', e.type);                    // "click"
    console.log('e.target:', e.target);                // Element that triggered event
    console.log('e.currentTarget:', e.currentTarget);  // Element with event listener

    console.log('=== EVENT METHODS ===');
    // e.preventDefault(); // Prevents default behavior
    // e.stopPropagation(); // Stops event from bubbling up
  };

  return (
    <div onClick={handleClick} style={{padding: '20px', border: '1px solid blue'}}>
      <h3>Parent Div</h3>
      <button onClick={handleClick}>Child Button</button>
    </div>
  );
};

e.target vs e.currentTarget:

const Example = () => {
  const handleClick = (e) => {
    console.log('target:', e.target.tagName);        // What you clicked
    console.log('currentTarget:', e.currentTarget.tagName); // What has the listener
  };

  return (
    <div onClick={handleClick}>  {/* currentTarget will be DIV */}
      <button>Click me</button>  {/* target will be BUTTON */}
    </div>
  );
};

preventDefault() – Stops Default Browser Behavior:

const FormExample = () => {
  const handleSubmit = (e) => {
    e.preventDefault(); // Prevents form from submitting and page refresh
    console.log('Form submitted via JavaScript instead!');
  };

  const handleLinkClick = (e) => {
    e.preventDefault(); // Prevents link from navigating
    console.log('Link clicked but not navigating!');
  };

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input type="text" />
        <button type="submit">Submit</button>
      </form>

      <a href="https://google.com" onClick={handleLinkClick}>
        This link won't navigate
      </a>
    </div>
  );
};

stopPropagation() – Stops Event Bubbling:

const BubblingExample = () => {
  const handleParentClick = () => {
    console.log('Parent clicked!');
  };

  const handleChildClick = (e) => {
    console.log('Child clicked!');
    e.stopPropagation(); // Prevents parent from also firing
  };

  return (
    <div onClick={handleParentClick} style={{padding: '20px', backgroundColor: 'lightblue'}}>
      Parent
      <button onClick={handleChildClick}>
        Child (click me - parent won't fire)
      </button>
    </div>
  );
};

e.type – Event Type:

const MultiEventExample = () => {
  const handleEvent = (e) => {
    switch(e.type) {
      case 'click':
        console.log('Button was clicked!');
        break;
      case 'mouseenter':
        console.log('Mouse entered button!');
        break;
      case 'mouseleave':
        console.log('Mouse left button!');
        break;
    }
  };

  return (
    <button 
      onClick={handleEvent}
      onMouseEnter={handleEvent}
      onMouseLeave={handleEvent}
    >
      Multi-event button
    </button>
  );
};

4. React Props 📦 Explained

Props (properties) are how you pass data from parent components to child components.

Basic Props Example:

// Parent component
const App = () => {
  const userName = "John";
  const userAge = 25;

  return (
    <div>
      <UserCard name={userName} age={userAge} />
    </div>
  );
};

// Child component receives props
const UserCard = (props) => {
  return (
    <div>
      <h2>Name: {props.name}</h2>
      <p>Age: {props.age}</p>
    </div>
  );
};

Props with Destructuring (More Common):

// Instead of using props.name, props.age
const UserCard = ({ name, age }) => {
  return (
    <div>
      <h2>Name: {name}</h2>
      <p>Age: {age}</p>
    </div>
  );
};

Different Types of Props:

const ComponentExample = () => {
  const user = { name: "Alice", email: "alice@example.com" };
  const numbers = [1, 2, 3, 4, 5];
  const isActive = true;

  return (
    <MyComponent 
      // String prop
      title="Hello World"

      // Number prop
      count={42}

      // Boolean prop
      isVisible={isActive}

      // Object prop
      user={user}

      // Array prop
      items={numbers}

      // Function prop
      onButtonClick={() => console.log('Clicked!')}
    />
  );
};

const MyComponent = ({ title, count, isVisible, user, items, onButtonClick }) => {
  return (
    <div>
      <h1>{title}</h1>
      <p>Count: {count}</p>
      {isVisible && <p>This is visible!</p>}
      <p>User: {user.name} ({user.email})</p>
      <ul>
        {items.map(item => <li key={item}>{item}</li>)}
      </ul>
      <button onClick={onButtonClick}>Click me</button>
    </div>
  );
};

Props in Our Todo App:

// We can break our todo app into smaller components
const TodoApp = () => {
  const [todos, setTodos] = useState([]);
  const [inputValue, setInputValue] = useState('');

  const addTodo = () => {
    // ... add todo logic
  };

  const deleteTodo = (id) => {
    // ... delete todo logic
  };

  return (
    <div>
      <AddTodoForm 
        value={inputValue}
        onChange={setInputValue}
        onAdd={addTodo}
      />
      <TodoList 
        todos={todos}
        onDelete={deleteTodo}
      />
    </div>
  );
};

// Child component receives props
const AddTodoForm = ({ value, onChange, onAdd }) => {
  return (
    <div>
      <input 
        type="text"
        value={value}
        onChange={(e) => onChange(e.target.value)}
      />
      <button onClick={onAdd}>Add Todo</button>
    </div>
  );
};

const TodoList = ({ todos, onDelete }) => {
  return (
    <ul>
      {todos.map(todo => (
        <TodoItem 
          key={todo.id}
          todo={todo}
          onDelete={onDelete}
        />
      ))}
    </ul>
  );
};

const TodoItem = ({ todo, onDelete }) => {
  return (
    <li>
      {todo.text}
      <button onClick={() => onDelete(todo.id)}>Delete</button>
    </li>
  );
};

Props Rules:

  1. Props are read-only – child components cannot modify props
  2. Props flow down – from parent to child, not the other way up
  3. Props can be any data type – strings, numbers, objects, arrays, functions
  4. Props are optional – you can provide default values
// Default props
const Greeting = ({ name = "World", enthusiasm = 1 }) => {
  return <h1>Hello {name}{"!".repeat(enthusiasm)}</h1>;
};

// Usage
<Greeting />                    // "Hello World!"
<Greeting name="Alice" />       // "Hello Alice!"
<Greeting name="Bob" enthusiasm={3} /> // "Hello Bob!!!"

🎯 Summary:

  1. Event Delegation = One listener handles many elements = Better performance
  2. Event parameter naming = Use any name you want (e, event, evt, v, etc.)
  3. Event methods: preventDefault() stops default behavior, stopPropagation() stops bubbling
  4. Event properties: target = what triggered event, currentTarget = what has listener
  5. Props = Data passed from parent to child components

Let’s see in Part 5. Happy React Development! 🚀

Unknown's avatar

Author: Abhilash

Hi, I’m Abhilash! A seasoned web developer with 13+ years of experience specializing in Ruby and Ruby on Rails. Since 2010, I’ve built scalable, robust web applications and worked with frameworks like Angular, Sinatra, Laravel, Node.js, and React. Passionate about clean, maintainable code and continuous learning, I share insights, tutorials, and experiences here. Let’s explore the ever-evolving world of web development together!

Leave a comment