Iniubong Obonguko Frontend developer, Vue ninja, code enthusiast. Learning every day.

Using custom events in React

5 min read 1426

Events are one of the integral features of the web as we know it. Without events, the web would most be terribly bland.

Events are actions that happen on a web page to HTML elements. Any time something happens on a web page, you can be sure it’s most likely an event, from when the page loads initially, to the user clicking a button, to closing or resizing a window. They’re all examples of different events that happen in the browser.

As a scripting language for the web, JavaScript lets us execute code in reaction to the occurrence of an event fired by HTML elements.

For JavaScript frameworks, events can also be used to pass data easily across an application. For example, React only supports unidirectional data flow, which means data can only pass from top level components to lower level components. But with events, you can listen for and react to certain events on any component level.

This article will cover how we can create and use custom events in React.

Contents

What are custom events?

The browser has a set number of default events available to various HTML elements. But then, in some cases, the need to create a custom event arises. For example, if you’d like to communicate to another component when a dialog component has been closed, the best way to achieve that would be through a custom event, as there is no such thing as an “onDialogClose” event in the browser.

Custom events are also known as “synthetic” events.

We can create custom events using the Event constructor. We can now finally create our non-existent “onDialogClose” event as such:

//First, we initialize our event
const event = new Event('onDialogClose');

// Next, we dispatch the event.
elem.dispatchEvent(event);

If we wanted to add a bit more data to our custom event, we could do so through the CustomEvent interface using the detail property:

//First, we initialize our event
const event = new CustomEvent('onDialogClose', {detail: "Main Dialog"});

// Next, we dispatch the event.
elem.dispatchEvent(event);

Check out this well-written article to learn more about custom events in JavaScript and how to implement them in your vanilla JavaScript projects.



Custom events in React

For this article, we’ll build a demo to illustrate how to make use of custom events in our React applications.

For the demo, our use case would be trying to display and hide a list of African countries, which will be contained is a CountryList.js component and declared in our application’s parent component App.js from another child component, ListControl.js.

For fetching the list of African countries, we’ll use the REST countries API.

App component diagram

Let’s dive in!

Project setup

First of all, we’ll create a new React project using Create React App.

Next, we’ll create a components folder in the src folder and create our components CountryList.js and ListControl.js inside of it.


More great articles from LogRocket:


The CountryList.js component will render the list of all countries in Africa from our API endpoint, and the ListControl.js component will contain buttons that will either display or hide the list, depending on its current state.

Also, in the root of our src folder, we’ll create an events.js file to abstract the code for emitting and listening for events, so our code is a bit cleaner and reusable.

Building a custom event in React

Once you’ve completed the instructions above, go ahead and copy and paste into your code editor the contents of the code blocks below. Don’t worry, we’ll go over everything in detail.

First, the event.js file:

//events.js

function subscribe(eventName, listener) {
  document.addEventListener(eventName, listener);
}

function unsubscribe(eventName, listener) {
  document.removeEventListener(eventName, listener);
}

function publish(eventName, data) {
  const event = new CustomEvent(eventName, { detail: data });
  document.dispatchEvent(event);
}

export { publish, subscribe, unsubscribe};

In the event.js file, as stated earlier, we’re only abstracting the logic for creating a custom event, dispatching the event, adding event listeners and removing event listeners for use in other components of our application.

You can rename the function names here to whatever you like, I just thought this naming pattern worked well with the publisher-subscriber pattern 😉

Next up is the CountryList.js component:

//CountryList.js

const CountryList = (props) => {
    return (
        <ul>
            <h2>List of countries in Africa</h2>
            {props.listData.map((el) => {
                return (
                    <li key={el.tld}>
                        <span>
                          <img src={el.flags.svg} alt={el.name.common}
                            style={{ width: "20px", marginRight: "5px" }} />
                        </span>
                        <span style={{fontSize: "20px"}}>{el.name.common} </span>
                    </li>
                );
            })}
        </ul>
    );
}
export default CountryList;

This component accepts a prop from its parent component which, according to our diagram above, is App.js. We’ll get to that in a bit.

It displays the data such as country name and flag from the API. I’ve gone ahead to add a bit of inline styling to the list item so it looks better.

Next is our ListControl.js component:

import { publish } from "../events"
const ListControl = (props) => {
  const showList = () => {
    publish('showList');
  }
  const hideList = () => {
    publish('hideList');
  }
  return (
    <div>
      { 
        props.listState ? <button onClick={hideList}>Hide List</button> :
        <button onClick={showList}>Show List</button>
       }
    </div>
  );
}
export default ListControl;

This component contains the button control logic for showing or hiding the list of countries.
It’s also where events for both actions will be dispatched.

We import the publish function from our events file and use it to create the custom events and dispatch them.

In addition, this component receives a prop from the App.js component that tells it when the list is rendered or not, and depending on the state of the list, renders one of two buttons: “Show list” and “Hide list”.

Last but not least, we have the parent component App.js:

import './App.css';
import { useState, useEffect } from "react";
import ListControl from './components/ListControl';
import CountryList from './components/CountryList';
import { subscribe, unsubscribe } from "./events";

const App = () => {
  const [isOpen, setIsOpen] = useState(false);
  const [countryList, setList] = useState([]);

  useEffect(() => {
    subscribe("showList", () => setIsOpen(true));
    subscribe("hideList", () => setIsOpen(false));

    async function fetchData() {
      const apiUrl = 'https://restcountries.com/v3.1/region/africa';
      const response = await fetch(apiUrl)
      let data = await response.json()
      setList(data)
    }
    fetchData()

    return () => {
      unsubscribe("showList");
      unsubscribe("hideList");
    }
  }, []);

  return (
    <div className="App">
      <h1>Using Custom Events In React</h1>
      <ListControl listState={isOpen}></ListControl>
      {
        isOpen ? <CountryList listData={countryList}></CountryList> :
          <h3>
            Click on the Button above to render the list of African Countries
          </h3>
      }
    </div>
  );
}
export default App;

The parent component first imports all the necessary components and React Hooks for it to function.

Next, we declare our app’s default state with the useState hook.

Remember the ListControl component which creates and dispatches (or “publishes”) the various custom events? Well, this is the component where we listen for and “subscribe” to those events.

Also, when our app is first loaded, the various event listeners get activated and then the fetchData function is triggered inside of the useEffect hook, which makes a HTTP request to the API and stores the returned data in our app’s state.

Basically, when the showLis``t event is published, the value of isOpen is changed to true, which in turn displays the list. And when the hideList event is published, the value of isOpen is changed to false and it hides the list component and returns a message instead.

Inside of the useEffect hook, we have a cleanup return function, which removes all event listeners when the component unmounts. This prevents multiple unused event listeners from being created, which would lead to a memory leak in our application.

And Voila! here’s the output of our work so far.

custom event react demo

Conclusion

In this article we learned about events and custom events in vanilla JavaScript. We also went further to discuss how we could implement the same pattern inside of a React application.

All the code for this tutorial is hosted on GitHub here. Feel free to fork it and play around with the code, and if you loved this tutorial, please do give it a star.

Also, feel free to leave your thoughts in the comment section below.

Thank you for reading!

Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — .

Iniubong Obonguko Frontend developer, Vue ninja, code enthusiast. Learning every day.

Leave a Reply