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 Handling Basics

🎯 Advanced Event Patterns — Mastering Event Architecture

Beyond basic event handling, production applications require patterns for composing events, custom event systems, and complex event flows. Understand how to design scalable event architectures.


🎪 Event Composition and Higher-Order Functions

Complex event handling often requires composing multiple handlers. Higher-order functions create reusable event handler factories that encapsulate cross-cutting concerns like logging, validation, and error handling.

The composition pattern allows chaining handlers, preventing default behavior conditionally, and maintaining single responsibility. Each handler does one thing well and can be composed with others.

// Event composition pattern: create specialized handlers from primitives
interface EventComposer {
  compose: (...handlers: ((e: any) => void)[]) => (e: any) => void;
  withLogging: (handler: (e: any) => void) => (e: any) => void;
  withValidation: (
    handler: (e: any) => void,
    validator: (e: any) => boolean
  ) => (e: any) => void;
}

const eventComposer: EventComposer = {
  // Compose multiple handlers to run in sequence
  compose:
    (...handlers) =>
    (e) => {
      handlers.forEach((h) => h(e));
    },

  // Add logging to any handler
  withLogging: (handler) => (e) => {
    console.log("Event:", e.type, "Target:", e.target);
    handler(e);
  },

  // Add validation gate
  withValidation: (handler, validator) => (e) => {
    if (validator(e)) {
      handler(e);
    }
  },
};

// Example: composed form handler with validation and logging
function AdvancedForm() {
  const handleFormValidation = (e: React.FormEvent) => {
    const form = e.currentTarget as HTMLFormElement;
    const data = new FormData(form);
    return Array.from(data.entries()).every(([key, value]) => value);
  };

  const handleFormSubmit = (e: React.FormEvent) => {
    console.log("Form submitted successfully");
  };

  const handleFormError = (e: React.FormEvent) => {
    console.error("Validation failed");
  };

  // Compose handlers: first validate, then submit or error
  const validatedSubmit = eventComposer.withValidation(
    eventComposer.withLogging(handleFormSubmit),
    handleFormValidation
  );

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (!handleFormValidation(e)) {
      handleFormError(e);
    } else {
      validatedSubmit(e);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="email" required />
      <input name="password" required />
      <button type="submit">Submit</button>
    </form>
  );
}

<details>

<summary>📚 More Examples</summary>

// Example 2: Chain-of-responsibility pattern for event handling
interface EventHandler {
  handle(e: React.MouseEvent): boolean; // Return true if handled
  setNext(handler: EventHandler): void;
}

class ClickHandler implements EventHandler {
  private next: EventHandler | null = null;

  handle(e: React.MouseEvent): boolean {
    if (e.button === 0) {
      console.log("Left click handled");
      return true;
    }
    return this.next ? this.next.handle(e) : false;
  }

  setNext(handler: EventHandler) {
    this.next = handler;
  }
}

class RightClickHandler implements EventHandler {
  private next: EventHandler | null = null;

  handle(e: React.MouseEvent): boolean {
    if (e.button === 2) {
      console.log("Right click handled");
      return true;
    }
    return this.next ? this.next.handle(e) : false;
  }

  setNext(handler: EventHandler) {
    this.next = handler;
  }
}

// Example 3: Decorator pattern for adding behavior
function withPreventDefault(
  handler: (e: React.FormEvent) => void
): (e: React.FormEvent) => void {
  return (e: React.FormEvent) => {
    e.preventDefault();
    handler(e);
  };
}

function withStopPropagation(
  handler: (e: React.MouseEvent) => void
): (e: React.MouseEvent) => void {
  return (e: React.MouseEvent) => {
    e.stopPropagation();
    handler(e);
  };
}

function withErrorBoundary(
  handler: (e: React.SyntheticEvent) => void
): (e: React.SyntheticEvent) => void {
  return (e: React.SyntheticEvent) => {
    try {
      handler(e);
    } catch (error) {
      console.error("Handler error:", error);
    }
  };
}

// Decorate handler with multiple behaviors
const robustSubmit = withPreventDefault(
  withErrorBoundary(
    (e: React.FormEvent) => {
      console.log("Form submitted");
    }
  )
);

</details>

💡 Custom Event Systems and Pub-Sub Pattern

Building applications with many components often requires a global event system for communication without prop drilling. The Pub-Sub (publish-subscribe) pattern decouples event producers from consumers.

// Global event bus using Pub-Sub pattern
interface Subscription {
  unsubscribe: () => void;
}

interface EventBus {
  on<T>(event: string, handler: (data: T) => void): Subscription;
  emit<T>(event: string, data: T): void;
  once<T>(event: string, handler: (data: T) => void): Subscription;
}

function createEventBus(): EventBus {
  const handlers = new Map<string, Set<Function>>();

  return {
    on<T>(event: string, handler: (data: T) => void): Subscription {
      if (!handlers.has(event)) {
        handlers.set(event, new Set());
      }
      handlers.get(event)!.add(handler);

      return {
        unsubscribe: () => {
          handlers.get(event)?.delete(handler);
        },
      };
    },

    emit<T>(event: string, data: T): void {
      handlers.get(event)?.forEach((handler) => {
        try {
          (handler as (data: T) => void)(data);
        } catch (error) {
          console.error(`Error in event handler for ${event}:`, error);
        }
      });
    },

    once<T>(event: string, handler: (data: T) => void): Subscription {
      const wrappedHandler = (data: T) => {
        handler(data);
        subscription.unsubscribe();
      };

      const subscription = this.on<T>(event, wrappedHandler);
      return subscription;
    },
  };
}

// Usage: Global event bus for app communication
const eventBus = createEventBus();

interface AuthEvent {
  userId: string;
  timestamp: number;
}

// Publisher: Authentication module
function LoginButton() {
  const handleLogin = () => {
    eventBus.emit<AuthEvent>("auth:login", {
      userId: "user123",
      timestamp: Date.now(),
    });
  };

  return <button onClick={handleLogin}>Login</button>;
}

// Subscriber: Notification module
function NotificationCenter() {
  useEffect(() => {
    const subscription = eventBus.on<AuthEvent>("auth:login", (data) => {
      console.log(`User ${data.userId} logged in`);
    });

    return () => subscription.unsubscribe();
  }, []);

  return null;
}

// Subscriber: Analytics module
function Analytics() {
  useEffect(() => {
    const loginSub = eventBus.on<AuthEvent>("auth:login", (data) => {
      console.log("Track: user login", data);
    });

    const logoutSub = eventBus.on<AuthEvent>("auth:logout", (data) => {
      console.log("Track: user logout", data);
    });

    return () => {
      loginSub.unsubscribe();
      logoutSub.unsubscribe();
    };
  }, []);

  return null;
}

<details>

<summary>📚 More Examples</summary>

// Example 2: Typed event bus using generics
interface EventMap {
  "user:created": { userId: string; email: string };
  "user:deleted": { userId: string };
  "post:published": { postId: string; title: string };
}

interface TypedEventBus {
  on<E extends keyof EventMap>(
    event: E,
    handler: (data: EventMap[E]) => void
  ): Subscription;
  emit<E extends keyof EventMap>(event: E, data: EventMap[E]): void;
}

function createTypedEventBus(): TypedEventBus {
  const handlers = new Map<string, Set<Function>>();

  return {
    on<E extends keyof EventMap>(
      event: E,
      handler: (data: EventMap[E]) => void
    ): Subscription {
      if (!handlers.has(event as string)) {
        handlers.set(event as string, new Set());
      }
      handlers.get(event as string)!.add(handler);

      return {
        unsubscribe: () => {
          handlers.get(event as string)?.delete(handler);
        },
      };
    },

    emit<E extends keyof EventMap>(event: E, data: EventMap[E]): void {
      handlers.get(event as string)?.forEach((handler) => {
        try {
          handler(data);
        } catch (error) {
          console.error(`Error in ${String(event)} handler:`, error);
        }
      });
    },
  };
}

// Type-safe usage
const bus = createTypedEventBus();

bus.on("user:created", (data) => {
  // data is typed as { userId: string; email: string }
  console.log("User created:", data.email);
});

bus.emit("user:created", {
  userId: "123",
  email: "user@example.com",
});

// This would be a TypeScript error:
// bus.emit("user:created", { userId: "123" }); // Missing email

</details>

🔧 Event Binding and Context Management

Event handlers need careful management of context and binding, especially in class components or when using callbacks. Modern React solutions use hooks to manage event handler dependencies.

// Context management in event handlers
interface User {
  id: string;
  name: string;
}

function UserActions() {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(false);

  // useCallback ensures handler identity is stable
  const handleSaveUser = useCallback(
    async (updatedUser: User) => {
      setLoading(true);
      try {
        const response = await fetch(`/api/users/${user?.id}`, {
          method: "PUT",
          body: JSON.stringify(updatedUser),
        });
        const result = await response.json();
        setUser(result);
      } catch (error) {
        console.error("Failed to save user:", error);
      } finally {
        setLoading(false);
      }
    },
    [user?.id]
  );

  const handleDeleteUser = useCallback(async () => {
    if (!user) return;

    setLoading(true);
    try {
      await fetch(`/api/users/${user.id}`, { method: "DELETE" });
      setUser(null);
    } catch (error) {
      console.error("Failed to delete user:", error);
    } finally {
      setLoading(false);
    }
  }, [user?.id]);

  return (
    <div>
      {user && (
        <>
          <UserProfile user={user} onSave={handleSaveUser} />
          <button onClick={handleDeleteUser} disabled={loading}>
            {loading ? "Deleting..." : "Delete User"}
          </button>
        </>
      )}
    </div>
  );
}

🎨 Advanced Event Delegation with Event Capturing

Understanding event flow (bubbling vs capturing phases) enables sophisticated event handling patterns. Capturing phase listeners execute first, allowing intervention before bubbling.

// Event capturing for modal/tooltip positioning
function PositionedTooltip() {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  const [visible, setVisible] = useState(false);

  // Capture phase handler to detect all clicks
  const handleCaptureClick = useCallback(
    (e: React.MouseEvent) => {
      const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
      setPosition({
        x: e.clientX - rect.left,
        y: e.clientY - rect.top,
      });
    },
    []
  );

  return (
    <div
      onClickCapture={handleCaptureClick}
      onClick={() => setVisible(!visible)}
      style={{ position: "relative" }}
    >
      <button>Hover me</button>
      {visible && (
        <div
          style={{
            position: "absolute",
            left: `${position.x}px`,
            top: `${position.y}px`,
          }}
        >
          Tooltip
        </div>
      )}
    </div>
  );
}

🔑 Key Takeaways

  • ✅ Compose event handlers using higher-order functions for reusability
  • ✅ Implement Pub-Sub pattern for decoupled component communication
  • ✅ Use TypeScript to ensure type-safe event handlers
  • ✅ Manage handler context carefully with useCallback dependencies
  • ✅ Understand capturing vs bubbling phase for advanced patterns
  • ✅ Create event buses for global application communication
  • ✅ Leverage chain-of-responsibility pattern for complex event flows

Ready to practice? Click Events (Advanced) | Challenges


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