search
HomeWeb Front-endJS TutorialEvent-Driven Architecture for Clean React Component Communication

Are you tired of the endless tangle of props drilling and callback chains in your React applications? Does managing state and communication between deeply nested components feel like wrestling with spaghetti code?

An event-driven architecture can simplify your component interactions, reduce complexity, and make your app more maintainable. In this article, I’ll show you how to use a custom useEvent hook to decouple components and improve communication across your React app.

Let me walk you through it, let's start from


The Problem: Props Drilling and Callback Chains

In modern application development, managing state and communication between components can quickly become cumbersome. This is especially true in scenarios involving props drilling—where data must be passed down through multiple levels of nested components—and callback chains, which can lead to tangled logic and make code harder to maintain or debug.

These challenges often create tightly coupled components, reduce flexibility, and increase the cognitive load for developers trying to trace how data flows through the application. Without a better approach, this complexity can significantly slow down development and lead to a brittle codebase.

The Traditional Flow: Props Down, Callbacks Up

In a typical React application, parent components pass props to their children, and children communicate back to the parent by triggering callbacks. This works fine for shallow component trees, but as the hierarchy deepens, things start to get messy:

Props Drilling: Data must be passed down manually through multiple levels of components, even if only the deepest component needs it.

Callback Chains: Similarly, child components must forward event handlers up the tree, creating tightly coupled and hard-to-maintain structures.

A Common Problem: Callback Complexity

Take this scenario, for example:

  • The Parent passes props to Children A.
  • From there, props are drilled down to GrandChildren A/B and eventually to SubChildren N.
  • If SubChildren N needs to notify the Parent of an event, it triggers a callback that travels back up through each intermediate component.

This setup becomes harder to manage as the application grows. Intermediate components often act as nothing more than middlemen, forwarding props and callbacks, which bloats the code and reduces maintainability.

Event-Driven Architecture for Clean React Component Communication

To address props drilling, we often turn to solutions like global state management libraries (e.g., Zustand) to streamline data sharing. But what about managing callbacks?

This is where an event-driven approach can be a game-changer. By decoupling components and relying on events to handle interactions, we can significantly simplify callback management. Let’s explore how this approach works.


The Solution: Enter the Event-Driven Approach

Event-Driven Architecture for Clean React Component Communication

Instead of relying on direct callbacks to communicate up the tree, an event-driven architecture decouples components and centralizes communication. Here’s how it works:

Event Dispatching

When SubChildren N triggers an event (e.g., onMyEvent), it doesn’t directly call a callback in the Parent.
Instead, it dispatches an event that is handled by a centralized Events Handler.

Centralized Handling

The Events Handler listens for the dispatched event and processes it.
It can notify the Parent (or any other interested component) or trigger additional actions as required.

Props Remain Downward

Props are still passed down the hierarchy, ensuring that components receive the data they need to function.

This can be solved with centralized state management tools like zustand, redux, but will not be covered in this article.


Implementation

But, how do we implement this architecture?

useEvent hook

Let's create a custom hook called useEvent, this hook will be responsible of handling event subscription and returning a dispatch function to trigger the target event.

As I am using typescript, I need to extend the window Event interface in order to create custom events:

interface AppEvent<payloadtype unknown> extends Event {
  detail: PayloadType;
}

export const useEvent = <payloadtype unknown>(
  eventName: keyof CustomWindowEventMap,
  callback?: Dispatch<payloadtype> | VoidFunction
) => {
  ...
};
</payloadtype></payloadtype></payloadtype>

By doing so, we can define custom events map and pass custom parameters:

interface AppEvent<payloadtype unknown> extends Event {
  detail: PayloadType;
}

export interface CustomWindowEventMap extends WindowEventMap {
  /* Custom Event */
  onMyEvent: AppEvent<string>; // an event with a string payload
}

export const useEvent = <payloadtype unknown>(
  eventName: keyof CustomWindowEventMap,
  callback?: Dispatch<payloadtype> | VoidFunction
) => {
  ...
};
</payloadtype></payloadtype></string></payloadtype>

Now that we defined needed interfaces, let's see the final hook code

import { useCallback, useEffect, type Dispatch } from "react";

interface AppEvent<payloadtype unknown> extends Event {
  detail: PayloadType;
}

export interface CustomWindowEventMap extends WindowEventMap {
  /* Custom Event */
  onMyEvent: AppEvent<string>;
}

export const useEvent = <payloadtype unknown>(
  eventName: keyof CustomWindowEventMap,
  callback?: Dispatch<payloadtype> | VoidFunction
) => {
  useEffect(() => {
    if (!callback) {
      return;
    }

    const listener = ((event: AppEvent<payloadtype>) => {
      callback(event.detail); // Use `event.detail` for custom payloads
    }) as EventListener;

    window.addEventListener(eventName, listener);
    return () => {
      window.removeEventListener(eventName, listener);
    };
  }, [callback, eventName]);

  const dispatch = useCallback(
    (detail: PayloadType) => {
      const event = new CustomEvent(eventName, { detail });
      window.dispatchEvent(event);
    },
    [eventName]
  );

  // Return a function to dispatch the event
  return { dispatch };
};

</payloadtype></payloadtype></payloadtype></string></payloadtype>

The useEvent hook is a custom React hook for subscribing to and dispatching custom window events. It allows you to listen for custom events and trigger them with a specific payload.

What we are doing here is pretty simple, we are using the standard event management system and extending it in order to accommodate our custom events.

Parameters:

  • eventName (string): The name of the event to listen for.
  • callback (optional): A function to call when the event is triggered, receiving the payload as an argument.

Features:

  • Event Listener: It listens for the specified event and calls the provided callback with the event's detail (custom payload).
  • Dispatching Events: The hook provides a dispatch function to trigger the event with a custom payload.

Example:

interface AppEvent<payloadtype unknown> extends Event {
  detail: PayloadType;
}

export const useEvent = <payloadtype unknown>(
  eventName: keyof CustomWindowEventMap,
  callback?: Dispatch<payloadtype> | VoidFunction
) => {
  ...
};
</payloadtype></payloadtype></payloadtype>

Ok cool but, what about a

Real World Example?

Check out this StackBlitz (if it does not load, please check it here)

This simple example showcases the purpose of the useEvent hook, basically the body's button is dispatching an event that is intercepted from Sidebar, Header and Footer components, that updates accordingly.

This let us define cause/effect reactions without the need to propagate a callback to many components.


Real-World Use Cases for useEvent

Here are some real-world use cases where the useEvent hook can simplify communication and decouple components in a React application:


1. Notifications System

A notification system often requires global communication.

  • Scenario:

    • When an API call succeeds, a "success" notification needs to be displayed across the app.
    • Components like a "Notifications Badge" in the header need to update as well.
  • Solution: Use the useEvent hook to dispatch an onNotification event with the notification details. Components like the NotificationBanner and Header can listen to this event and update independently.

2. Theme Switching

When a user toggles the theme (e.g., light/dark mode), multiple components may need to respond.

  • Scenario:

    • A ThemeToggle component dispatches a custom onThemeChange event.
    • Components like the Sidebar and Header listen for this event and update their styles accordingly.
  • Benefits: No need to pass the theme state or callback functions through props across the entire component tree.

3. Global Key Bindings

Implement global shortcuts, such as pressing "Ctrl S" to save a draft or "Escape" to close a modal.

  • Scenario:
    • A global keydown listener dispatches an onShortcutPressed event with the pressed key details.
    • Modal components or other UI elements respond to specific shortcuts without relying on parent components to forward the key event.

4. Real-Time Updates

Applications like chat apps or live dashboards require multiple components to react to real-time updates.

  • Scenario:
    • A WebSocket connection dispatches onNewMessage or onDataUpdate events when new data arrives.
    • Components such as a chat window, notifications, and unread message counters can independently handle updates.

5. Form Validation Across Components

For complex forms with multiple sections, validation events can be centralized.

  • Scenario:
    • A form component dispatches onFormValidate events as users fill out fields.
    • A summary component listens for these events to display validation errors without tightly coupling with form logic.

6. Analytics Tracking

Track user interactions (e.g., button clicks, navigation events) and send them to an analytics service.

  • Scenario:
    • Dispatch onUserInteraction events with relevant details (e.g., the clicked button’s label).
    • A central analytics handler listens for these events and sends them to an analytics API.

7. Collaboration Tools

For collaborative tools like shared whiteboards or document editors, events can manage multi-user interactions.

  • Scenario:
    • Dispatch onUserAction events whenever a user draws, types, or moves an object.
    • Other clients and UI components listen for these events to reflect the changes in real time.

By leveraging the useEvent hook in these scenarios, you can create modular, maintainable, and scalable applications without relying on deeply nested props or callback chains.


Conclusions

Events can transform the way you build React applications by reducing complexity and improving modularity. Start small—identify a few components in your app that would benefit from decoupled communication and implement the useEvent hook.

With this approach, you’ll not only simplify your code but also make it easier to maintain and scale in the future.

Why Use Events?
Events shine when you need your components to react to something that happened elsewhere in your application, without introducing unnecessary dependencies or convoluted callback chains. This approach reduces the cognitive load and avoids the pitfalls of tightly coupling components.

My Recommendation
Use events for inter-component communication—when one component needs to notify others about an action or state change, regardless of their location in the component tree.
Avoid using events for intra-component communication, especially for components that are closely related or directly connected. For these scenarios, rely on React's built-in mechanisms like props, state, or context.

A Balanced Approach
While events are powerful, overusing them can lead to chaos. Use them judiciously to simplify communication across loosely connected components, but don’t let them replace React’s standard tools for managing local interactions.

The above is the detailed content of Event-Driven Architecture for Clean React Component Communication. For more information, please follow other related articles on the PHP Chinese website!

Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Python vs. JavaScript: Development Environments and ToolsPython vs. JavaScript: Development Environments and ToolsApr 26, 2025 am 12:09 AM

Both Python and JavaScript's choices in development environments are important. 1) Python's development environment includes PyCharm, JupyterNotebook and Anaconda, which are suitable for data science and rapid prototyping. 2) The development environment of JavaScript includes Node.js, VSCode and Webpack, which are suitable for front-end and back-end development. Choosing the right tools according to project needs can improve development efficiency and project success rate.

Is JavaScript Written in C? Examining the EvidenceIs JavaScript Written in C? Examining the EvidenceApr 25, 2025 am 12:15 AM

Yes, the engine core of JavaScript is written in C. 1) The C language provides efficient performance and underlying control, which is suitable for the development of JavaScript engine. 2) Taking the V8 engine as an example, its core is written in C, combining the efficiency and object-oriented characteristics of C. 3) The working principle of the JavaScript engine includes parsing, compiling and execution, and the C language plays a key role in these processes.

JavaScript's Role: Making the Web Interactive and DynamicJavaScript's Role: Making the Web Interactive and DynamicApr 24, 2025 am 12:12 AM

JavaScript is at the heart of modern websites because it enhances the interactivity and dynamicity of web pages. 1) It allows to change content without refreshing the page, 2) manipulate web pages through DOMAPI, 3) support complex interactive effects such as animation and drag-and-drop, 4) optimize performance and best practices to improve user experience.

C   and JavaScript: The Connection ExplainedC and JavaScript: The Connection ExplainedApr 23, 2025 am 12:07 AM

C and JavaScript achieve interoperability through WebAssembly. 1) C code is compiled into WebAssembly module and introduced into JavaScript environment to enhance computing power. 2) In game development, C handles physics engines and graphics rendering, and JavaScript is responsible for game logic and user interface.

From Websites to Apps: The Diverse Applications of JavaScriptFrom Websites to Apps: The Diverse Applications of JavaScriptApr 22, 2025 am 12:02 AM

JavaScript is widely used in websites, mobile applications, desktop applications and server-side programming. 1) In website development, JavaScript operates DOM together with HTML and CSS to achieve dynamic effects and supports frameworks such as jQuery and React. 2) Through ReactNative and Ionic, JavaScript is used to develop cross-platform mobile applications. 3) The Electron framework enables JavaScript to build desktop applications. 4) Node.js allows JavaScript to run on the server side and supports high concurrent requests.

Python vs. JavaScript: Use Cases and Applications ComparedPython vs. JavaScript: Use Cases and Applications ComparedApr 21, 2025 am 12:01 AM

Python is more suitable for data science and automation, while JavaScript is more suitable for front-end and full-stack development. 1. Python performs well in data science and machine learning, using libraries such as NumPy and Pandas for data processing and modeling. 2. Python is concise and efficient in automation and scripting. 3. JavaScript is indispensable in front-end development and is used to build dynamic web pages and single-page applications. 4. JavaScript plays a role in back-end development through Node.js and supports full-stack development.

The Role of C/C   in JavaScript Interpreters and CompilersThe Role of C/C in JavaScript Interpreters and CompilersApr 20, 2025 am 12:01 AM

C and C play a vital role in the JavaScript engine, mainly used to implement interpreters and JIT compilers. 1) C is used to parse JavaScript source code and generate an abstract syntax tree. 2) C is responsible for generating and executing bytecode. 3) C implements the JIT compiler, optimizes and compiles hot-spot code at runtime, and significantly improves the execution efficiency of JavaScript.

JavaScript in Action: Real-World Examples and ProjectsJavaScript in Action: Real-World Examples and ProjectsApr 19, 2025 am 12:13 AM

JavaScript's application in the real world includes front-end and back-end development. 1) Display front-end applications by building a TODO list application, involving DOM operations and event processing. 2) Build RESTfulAPI through Node.js and Express to demonstrate back-end applications.

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

WebStorm Mac version

WebStorm Mac version

Useful JavaScript development tools

mPDF

mPDF

mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

EditPlus Chinese cracked version

EditPlus Chinese cracked version

Small size, syntax highlighting, does not support code prompt function

DVWA

DVWA

Damn Vulnerable Web App (DVWA) is a PHP/MySQL web application that is very vulnerable. Its main goals are to be an aid for security professionals to test their skills and tools in a legal environment, to help web developers better understand the process of securing web applications, and to help teachers/students teach/learn in a classroom environment Web application security. The goal of DVWA is to practice some of the most common web vulnerabilities through a simple and straightforward interface, with varying degrees of difficulty. Please note that this software

SublimeText3 English version

SublimeText3 English version

Recommended: Win version, supports code prompts!