Ojasa Mirai

Ojasa Mirai

ReactJS

Loading...

Learning Level

🟢 Beginner🔵 Advanced
🎪 Event Handling Basics🖱️ Click Events⌨️ Keyboard Events📝 Form Events🎯 Event Handlers with Parameters🚫 Event Default Behavior🔗 Event Delegation⚡ Performance & Events
Reactjs/Events/Event Performance

⚡ Performance & Events — Writing Efficient Event Handlers

Event handlers are called frequently. Learn best practices to avoid performance problems and unnecessary re-renders.


🎯 Named Functions vs Inline Functions

Creating new functions on every render can impact performance. Use named functions when possible:

// ❌ Less efficient: new function every render
function ListBad() {
  const items = [1, 2, 3];

  return (
    <ul>
      {items.map((item) => (
        <li
          key={item}
          onClick={() => console.log("Clicked:", item)}
        >
          {item}
        </li>
      ))}
    </ul>
  );
}

// ✅ Better: use named function
function ListGood() {
  const items = [1, 2, 3];

  const handleItemClick = (item) => {
    console.log("Clicked:", item);
  };

  return (
    <ul>
      {items.map((item) => (
        <li
          key={item}
          onClick={() => handleItemClick(item)}
        >
          {item}
        </li>
      ))}
    </ul>
  );
}

Actually, for simple cases, inline functions are fine. The key is avoiding re-renders of child components.

<details>

<summary>📚 More Examples</summary>

// For child components that memoize, use useCallback
import { useCallback } from "react";

function ParentWithMemo() {
  const handleClick = useCallback((id) => {
    console.log("Clicked:", id);
  }, []); // Empty dependency array = function never changes

  return (
    <div>
      <MemoizedChild onClick={handleClick} />
    </div>
  );
}

// The child can use memo to prevent re-rendering
const MemoizedChild = memo(({ onClick }) => {
  // Only re-renders if onClick changes
  return <button onClick={() => onClick(1)}>Click</button>;
});

</details>

💡 Avoid Creating Objects in Handlers

Creating new objects in handlers can trigger unnecessary updates:

// ❌ Avoid: creating new object on every click
function BadForm() {
  const handleSubmit = (e) => {
    e.preventDefault();
    const formData = {
      name: e.target.name.value,
      email: e.target.email.value,
    };
    console.log(formData);
  };

  return <form onSubmit={handleSubmit}>{/* form fields */}</form>;
}

// ✅ Better: use FormData API
function GoodForm() {
  const handleSubmit = (e) => {
    e.preventDefault();
    const data = new FormData(e.target);
    const formData = Object.fromEntries(data);
    console.log(formData);
  };

  return <form onSubmit={handleSubmit}>{/* form fields */}</form>;
}

🎨 Debouncing for High-Frequency Events

Some events fire very often (scroll, resize, mouse move). Debouncing delays execution:

function SearchInput() {
  const [query, setQuery] = useState("");
  const [results, setResults] = useState([]);

  // Simple debounce: only update after user stops typing
  const handleChange = (e) => {
    const value = e.target.value;
    setQuery(value);

    // Cancel previous timeout
    clearTimeout(handleChange.timeoutId);

    // Set new timeout
    handleChange.timeoutId = setTimeout(() => {
      console.log("Searching for:", value);
      // Perform search here
    }, 500);
  };

  return (
    <div>
      <input
        value={query}
        onChange={handleChange}
        placeholder="Search (waits 500ms)..."
      />
    </div>
  );
}

<details>

<summary>📚 More Examples</summary>

// Better debounce using a hook
import { useCallback } from "react";

function useDebounce(callback, delay) {
  const timeoutId = useCallback(
    (value) => {
      const id = setTimeout(() => callback(value), delay);
      return () => clearTimeout(id);
    },
    [callback, delay]
  );
  return timeoutId;
}

function SearchWithHook() {
  const [query, setQuery] = useState("");

  const debouncedSearch = useDebounce((value) => {
    console.log("Searching for:", value);
  }, 500);

  const handleChange = (e) => {
    setQuery(e.target.value);
    debouncedSearch(e.target.value);
  };

  return <input onChange={handleChange} value={query} />;
}

</details>

🔧 Event Delegation for Lists

Attaching handlers to parent elements is more efficient than to each child:

// ❌ Less efficient: handler on each item
function ListBad() {
  const items = Array.from({ length: 1000 }, (_, i) => i);

  return (
    <ul>
      {items.map((item) => (
        <li key={item} onClick={() => console.log("Item:", item)}>
          Item {item}
        </li>
      ))}
    </ul>
  );
}

// ✅ Better: single handler on parent
function ListGood() {
  const items = Array.from({ length: 1000 }, (_, i) => i);

  const handleListClick = (e) => {
    const li = e.target.closest("li");
    if (li) {
      console.log("Item:", li.textContent);
    }
  };

  return (
    <ul onClick={handleListClick}>
      {items.map((item) => (
        <li key={item}>Item {item}</li>
      ))}
    </ul>
  );
}

🎨 Preventing Unnecessary Re-renders

Not all state updates need to update the component:

function Counter() {
  const [count, setCount] = useState(0);

  // Track clicks externally without updating component
  const clickCountRef = useRef(0);

  const handleClick = () => {
    clickCountRef.current += 1;
    // Component doesn't re-render

    if (clickCountRef.current % 5 === 0) {
      setCount(count + 1); // Only update state every 5 clicks
    }
  };

  return (
    <div>
      <p>Major count: {count}</p>
      <p>Total clicks: {clickCountRef.current}</p>
      <button onClick={handleClick}>Click</button>
    </div>
  );
}

📊 Best Practices Checklist

PracticeWhyExample
Use event delegationReduces event listenersOne handler on parent for list items
Debounce high-frequency eventsPrevents performance lagSearch input with 500ms delay
Avoid creating objects in handlersPrevents unnecessary updatesUse FormData API instead
Use named functions for complex logicEasier to debugExtract handler logic to function
Use useCallback for child componentsPrevents re-renders if child memoizesuseCallback with empty deps for stable identity

🎨 Real-World Example: Efficient Todo List

function EfficientTodoList() {
  const [todos, setTodos] = useState([
    { id: 1, text: "Learn React" },
    { id: 2, text: "Build app" },
  ]);

  // Single handler for all actions using delegation
  const handleListAction = (e) => {
    const button = e.target.closest("button");
    if (!button) return;

    const id = parseInt(button.dataset.id);
    const action = button.dataset.action;

    if (action === "delete") {
      setTodos(todos.filter((t) => t.id !== id));
    }
  };

  // Search with debouncing
  const [searchQuery, setSearchQuery] = useState("");

  const handleSearch = (e) => {
    const value = e.target.value;
    setSearchQuery(value);

    clearTimeout(handleSearch.timeoutId);
    handleSearch.timeoutId = setTimeout(() => {
      console.log("Filtering for:", value);
    }, 300);
  };

  return (
    <div>
      <input
        onChange={handleSearch}
        placeholder="Search..."
      />
      <ul onClick={handleListAction}>
        {todos.map((todo) => (
          <li key={todo.id}>
            {todo.text}
            <button data-id={todo.id} data-action="delete">
              Delete
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

🔑 Key Takeaways

  • ✅ Use event delegation for lists to reduce event listeners
  • ✅ Debounce high-frequency events like search or scroll
  • ✅ Avoid creating objects inside handlers when possible
  • ✅ Use named functions for complex handler logic
  • ✅ Use useCallback only when child components memoize
  • ✅ Measure performance with React DevTools Profiler
  • ✅ Simple inline functions are usually fine for single-use handlers

Ready to practice? Challenges | View All Topics


Resources

Python Docs

Ojasa Mirai

Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.

Learn Deep • Build Real • Verify Skills • Launch Forward

Courses

PythonFastapiReactJSCloud

© 2026 Ojasa Mirai. All rights reserved.

TwitterGitHubLinkedIn