Decoupling React Components with an Event Bus

Radovan Stevanovic
JavaScript in Plain English
4 min readJan 26, 2023

--

An event bus is a pattern used to decouple components and communicate with each other through a shared, centralized event bus. This can help manage shared states in a front-end application and make it more testable. This guide will examine how to use event bus in a React application and its benefits. We will also show how to implement it using popular libraries, such as EventEmitter or rxjs and provide examples of how it can be used to solve common challenges in managing shared states.

Setting up an Event Bus in a React Application using TypeScript

The first step in using an event bus in a React application is to set it up. There are several libraries available for implementing an event bus in JavaScript, such as EventEmitter and rxjs. In this guide, we will be using rxjs library for implementing event bus because of its robustness and support for TypeScript.

To set up an event bus using rxjs, we need to install the rxjs library by running the following command in the terminal:

yarn add rxjs

Once rxjs is installed, we can import it in our application and create a new instance of the Subject class. A Subject is a special type of Observable that allows values to be multicasted to many Observers.

import { Subject } from 'rxjs';

const eventBus = new Subject();

We can now use the eventBus object to emit and subscribe to events in our application.

             +-------------+
| |
| Subject |
| |
+------+-------+
|
|
+------v-------+
| |
| Observer(s) |
| |
+-------------+

Emitting Events

Once the event bus is set up, we can emit events from different parts of our application. To emit an event, we can use the next method of the eventBus object, passing the event data as an argument.

eventBus.next({ type: 'LOGIN', payload: { username: 'johndoe' } });

Using an enumeration or string constants for event types is good practice. This will help catch errors early on during development and make your code more readable.

enum EventTypes {
LOGIN = 'LOGIN',
LOGOUT = 'LOGOUT',
//... more events
}

eventBus.next({ type: EventTypes.LOGIN, payload: { username: 'johndoe' } });
             +-------------+         +-----------+
| | | |
| Subject | | Component |
| | | |
+------+-------+ +-----+-----+
| |
| next(event) |
| |
+------v-------+ +-----+-----+
| | | |
| Observer(s) | | Component |
| | | |
+-------------+ +-----------+

Subscribing to Events

For components to receive and respond to events emitted by the event bus, they need to subscribe to the event bus. To subscribe to an event, we can use the subscribe method of the eventBus object and pass a callback function that will be invoked when the event is emitted.

eventBus.subscribe((event) => {
switch (event.type) {
case EventTypes.LOGIN:
console.log(`User ${event.payload.username} has logged in`);
break;
case EventTypes.LOGOUT:
console.log(`User ${event.payload.username} has logged out`);
break;
default:
break;
}
});

It’s recommended to handle the events in a separate service or store to avoid polluting the component with business logic and make it more testable.

             +-------------+         +-----------+
| | | |
| Subject | | Component |
| | | |
+------+-------+ +-----+-----+
| |
| |
| |
+------v-------+ +-----+-----+
| | | |
| Observer(s) | | Component |
| | | |
+-------------+ | subscribe |
+-----+-----+

Benefits of using an Event Bus in a React Application

  1. Decoupling: An event bus allows different parts of an application to communicate with each other without being tightly coupled. This can make it easier to manage shared states and make the application more testable.
  2. Centralized Event Handling: Having a central event bus makes it easier to manage and track the events happening in the application. This can be helpful for debugging and monitoring.
  3. Asynchronous Actions: An event bus can consistently handle asynchronous actions across different application parts, such as API calls.
  4. Improved Performance: By decoupling the components, they can be rendered independently. This can improve the overall performance of the application.
  5. Better Code organization: By separating the event handling logic from the components and putting it in a separate service or store and making abstraction using custom hooks like useEvents with a predicate function to filter the events of interest, the code becomes more organized and easier to maintain.
import { useState, useEffect } from 'react';
import { Subject } from 'rxjs';

const eventBus = new Subject();

const useEvents = (predicateFn) => {
const [events, setEvents] = useState([]);

useEffect(() => {
const subscription = eventBus.pipe(filter(predicateFn)).subscribe(setEvents);
return () => subscription.unsubscribe();
}, [predicateFn]);

const publish = (event) => eventBus.next(event);

return { events, publish };
};

Final Words

In this guide, we have looked at how to use an event bus in a React application using TypeScript and rxjs library. We have seen how an event bus can decouple different parts of an application, making it easier to manage shared states and make the application more testable and maintainable. We've also seen how to set up an event bus, emit events, and subscribe to events in a React application. And how to make abstractions using custom hooks to filter the events of interest.

The event bus is a powerful pattern that can help you manage your application's complexity and make it more scalable and maintainable. By following the best practices and utilizing the right tools, you can implement an event bus in your React application and enjoy its benefits.

I hope you found this guide helpful and that it gives you a good starting point for implementing an event bus in your React application.

More content at PlainEnglish.io.

Sign up for our free weekly newsletter. Follow us on Twitter, LinkedIn, YouTube, and Discord.

Build awareness and adoption for your tech startup with Circuit.

--

--

Curiosity in functional programming, cybersecurity, and blockchain drives me to create innovative solutions and continually improve my skills