DEV Community

Cover image for Event Emitter with Typescript- Advanced Usage
Ritik Banger
Ritik Banger

Posted on

Event Emitter with Typescript- Advanced Usage

Passing functional props to great grand child components and then invoking the function on some data changes is hectic and do side effects. Also, such passing down of props is not a very good way to write react or typescript code.

Here comes the event emitter. Event Emitter is a common term if you are working with an Angular or NodeJs project but when it comes to react, developers hardly heard this term. So, let me straight down to what event emitter is, what it do, how it do?

What is an Event Emitter?

An event emitter is a code pattern that listens to a named event, fires (or calls) a callback function, then emits that event with a value. Sometimes this is referred to as a “pub/sub (publisher-subscriber)” model, or listener.

Why do we need Event Emitter?

They are very useful when you have some function that needs to execute “whenever this other thing happens”, without requiring that function to finish or even work for that matter.

What Event Emitter do?

Event Emitter solve complex business solutions that requires to invoke certain functionality on the basis of change in some other thing.

A complex Use Case: I have a sale post component in which I can post some title, price and images and other users can offer me a price to buy the item. Now, I wish to list down all the offers in the form of comments below the post, then this can be done with the help of backend.
If somebody gives an offer then save the offer detail in the offer table and also save a comment User.name gives $offer in the comments table in database.

Now there exists an entry for every offer in the comments table but the comment component on the frontend does not know this. Here, event emitter can helps. Whenever somebody gives an offer, emit an event to re-fetch the comments and thus a complex problem to show offer comments as soon as an offer is given is solved.

How Event Emitter works?

Now, let us jump to the coding part. I know it may be complex for some users to understand this as it is advanced react, but still in many cases it helps.

1. We will create a file eventEmitter.ts that will be the core part of our event emitter.

In this we will have an enum EventType that will keep track of events similar to action types set as variables in redux.

Then we have a complex object eventEmitter, Many of you would wonder that I have created an object in which I am executing functions, quite complex but cool.

We have an events properties that is basically another object that will list the event name with their respective callbacks. It is readonly as we do not wish it to be changed by an outside function. It is similar to the use of private access modifier in classes.

After that we have dispatch function that will dispatch the event and call the callback for every event.

Subscribe will subscribe the event with a specific callback and Unsubscribe will unsubscribe the event to avoid unnecessary event calls.

export enum EventType {
  REFETCH_COMMENT = 'refetchComment',
}

/**
 * Event emitter to subscribe, dispatch, and unsubscribe to events.
 */
export const eventEmitter: {
  readonly events: Record<string, (() => void)[]>
  dispatch(eventType: EventType, uniqueSuffix: string | number): void
  subscribe(eventType: EventType, uniqueSuffix: string | number, callback: () => void): void
  unsubscribe(eventType: EventType, uniqueSuffix: string | number): void
  getEventKey(eventType: EventType, uniqueSuffix: string | number): string
} = {
  //This is event object to store events.
  events: {},
  //Internal function to get event name from type and suffix
  getEventKey(eventType: EventType, uniqueSuffix: string | number) {
    return `${eventType} ${uniqueSuffix}`
  },
  //This will dispatch the event and call the callback for every event.
  dispatch(event, uniqueSuffix) {
    const eventName = this.getEventKey(event, uniqueSuffix)
    if (!this.events[eventName]) return
    this.events[eventName].forEach((callback: () => void) => callback())
  },
  //This will subscribe the event with a specific callback
  subscribe(event, uniqueSuffix, callback) {
    const eventName = this.getEventKey(event, uniqueSuffix)
    if (!this.events[eventName]) this.events[eventName] = []
    if (!this.events[eventName]?.includes(this.events[eventName][0])) this.events[eventName]?.push(callback)
  },
  //This will unsubscribe the event to avoid unnecessary event calls
  unsubscribe(event, uniqueSuffix) {
    const eventName = this.getEventKey(event, uniqueSuffix)
    if (!this.events[eventName]) return
    delete this.events[eventName]
  },
}

Enter fullscreen mode Exit fullscreen mode

2. Now, In the offer component where we would be sending offers, we will dispatch the events and unsubscribe the events after dispatch like this:

eventEmitter.dispatch(EventType.REFETCH_COMMENT, uniqueSuffix)
eventEmitter.unsubscribe(EventType.REFETCH_COMMENT, uniqueSuffix)

Enter fullscreen mode Exit fullscreen mode

3. Here, we would be subscribing the event with a callback in the comments component that will re-fetch the comments.

 eventEmitter.subscribe(EventType.REFETCH_COMMENT, uniqueSuffix, () => fetchLatestPostComments())
Enter fullscreen mode Exit fullscreen mode

Here, fetchLatestPostComments is the function that will refetch the comments from the backend.

This is how we have solved a complex business problem with the help of event emitters.

Though, with day-to-day developments, mutation comes into existence and these complex tasks can be performed by packages like React-Query too.

Top comments (0)