Decoupling React Components with an Event Bus
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
- 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.
- 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.
- Asynchronous Actions: An event bus can consistently handle asynchronous actions across different application parts, such as API calls.
- Improved Performance: By decoupling the components, they can be rendered independently. This can improve the overall performance of the application.
- 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.