Dispatching Events With React and TypeScript

Pascal Allen
3 min readJul 17, 2023

--

This publication demonstrates a simple way to dispatch and listen to events with React and TypeScript. For simplicity, I will assume that you have already set up a React and TypeScript project of which you want event dispatching capabilities. This publication is also based on the rxjs library, which we will detail below.

Prerequisites

Install rxjs

RxJS is a library for composing asynchronous and event-based programs by using observable sequences.

rxjs is also maintained regularly, and is easy to use. You can view install instructions, as well as documentation, on their website here. Let’s begin by adding the rxjs and react-rxjs libraries to our project by running the following command from your project directory containing your package.json file:

npm i rxjs @react-rxjs/core @react-rxjs/utils

Define an Event Enum

Firstly, let’s leverage TypeScript to create an enum called DomainEvents, so that all of our possible events are defined in one area, like so:

// DomainEvents.ts

export enum DomainEvents {
ALL = 'ALL',
USER_CREATED = 'USER_CREATED'
}

Create an Event Dispatcher

Since dispatching events is a repeated process, let’s create a file called eventDispatcher.ts and add the following code:

import { createSignal } from '@react-rxjs/utils';
import { merge } from 'rxjs';
import { v4 as uuid } from 'uuid'; // an arbitrary library for creating UUIDs
import { DomainEvents } from '@domain/constants/DomainEvents';

export type DomainEvent = {
id?: string;
name: DomainEvents;
data: {
[key: string]: unknown;
};
};

const [event$, setEvent] = createSignal<DomainEvent | undefined>();

export const eventMap$ = merge(event$);

const dispatch = (event: DomainEvent): void => {
if (!event.id) {
event.id = uuid();
}
setEvent(event);
setEvent(undefined);
};

export default Object.freeze({
dispatch
});

The code above that you just added to your eventDispatcher.ts file defines and exports DomainEvent, eventMap$, and dispatch, which we will use to create and dispatch events.

Create an Event Listener

Since listening for specific events is a repeated process, let’s create a file called useEvents.ts and add the following code:

import { useState, useEffect } from 'react';
import { DomainEvents } from '@domain/constants/DomainEvents';
import { eventMap$, DomainEvent } from '@services/events/eventDispatcher';

type EventState = {
current?: DomainEvent;
};

const initialEventState: EventState = {};

const useEvent = (name: DomainEvents) => {
const [current, setCurrent] = useState(initialEventState.current);

useEffect(() => {
const subscription = eventMap$.subscribe(eventData => {
if (eventData === undefined) {
return setCurrent(undefined);
}
if ((name === DomainEvents.ALL || name === eventData?.name) && current?.id !== eventData?.id) {
setCurrent(eventData);
}
});

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

return current;
};

export default useEvent;

The code above that you just added to your useEvents.ts file defines and exports useEvent, which we will use to listen for specific events.

Dispatching Events

Now that we’ve created an event dispatcher, we can use it to dispatch events like so:

import { DomainEvents } from '@domain/constants/DomainEvents';
import eventDispatcher from '@services/events/eventDispatcher';

// logic for user creation should occur here.
// i.e. a request to an API endpoint, with a successful
// response back (containing the created user's ID).

eventDispatcher.dispatch({
name: DomainEvents.USER_CREATED,
data: {
id: response.body.data.id
}
});

The code snippet above is pseudocode that dispatches a USER_CREATED event. This is pseudocode, but most of this can be modified and used in a real-world application — just remember to add logic for your specific use-case, and handle errors appropriately.

Listening for Events

Let’s look at how we can leverage our custom useEvent hook to listen for a specific event:

import React, { useEffect } from 'react';
import { DomainEvents } from '@domain/constants/DomainEvents';
import useEvent from '@hooks/useEvents';
import { DomainEvent } from '@services/events/eventDispatcher';

const userCreatedEvent: DomainEvent | undefined = useEvent(DomainEvents.USER_CREATED);

useEffect(() => {
if (userCreatedEvent !== undefined) {
// do something
}
}, [userCreatedEvent]);

The code snippet above is pseudocode that listens for the USER_CREATED event. When the USER_CREATED event occurs, the useEffect is triggered to do something specific for your use-case — since we have specified userCreatedEvent as a dependency. Again, this is pseudocode, but most of this can be modified and used in a real-world application — just remember to modify it to your needs.

Conclusion

This tutorial is meant to be demonstrative of how to create a simple event dispatcher with React and TypeScript. This is just one way to implement this pattern, there are many other possible implementations but hopefully this gets you started. Thanks and have a great day!

--

--

Pascal Allen
Pascal Allen

Responses (1)