Categories
Programming

Event Observer Pattern Uses In ReactJS

As we solve the issue of state management, Sometime we try to implement the stuff with some patterns. Event Observer is one of them and very powerful pattern.

We can implement a lot of state related things in React with different kind of approaches Example => Prop Drilling with Context, Data providing and state lifestyles with Redux. And There are so many ways.

But sometimes we have some common requirements for the complete application. And we want to call this kind of function/method from multiple places.

Showing notification/Toast is one of that common requirement. And we need to have accessible function/method on almost all part of the application. And sometimes out of the context of ReactJS.

So today we will implement the Event Observer pattern to show the notification/toast in the application from any part of the application.

For this to complete first of all we should have some common methods of the eventObserver, So that we can see the Event Observer uses in ReactJs.

Some common methods emit, attach, detach. Here first we need to have a way to attach an observer with the eventName and then we can emit the some data for that attached observer. And when needed we can remove the specific attached observer for specific event. Below is the implementation for it.

const events = {};

const eventManager = {
  emit: function (eventName, data) {
    const observers = events[eventName] || [];
    observers.forEach((callback) => {
      callback(data);
    });
  },
  attach: function (eventName, callback) {
    const previousEventObservers = events[eventName] || [];
    // Attach observer to the event
    events[eventName] = [...previousEventObservers, callback];

    // Return callback instance to be removed when needed
    return callback;
  },

  detach: function (eventName, callback) {
    const observers = events[eventName] || [];
    const filteredObservers = observers.filter(
      (observer) => observer !== callback
    );

    events[eventName] = filteredObservers;
  }
};
export { eventManager };


This is the simplest implementation for Event Observer. Now let’s use it in the ReactJs Component so, To properly show the demo we need to have one parent component i.e. App, some child components SomeChild1, SomeChild2 to show the demo.

Notifications could be emitted from anywhere in the tree of components Or Could be triggered from any other places, from any promise or from anywhere Even out of the context of the ReactJS.

This is the simple Demo.

import { useEffect, useState } from "react";
import "./styles.css";
import { eventManager } from "./EventManager";

const NOTIFICATION_EVENT_NAME = "notificaiton";
export default function App() {
  return (
    <div className="App">
      <h1>Event Observer Uses</h1>
      <Notifications />
      <SomeChild1 />
    </div>
  );
}

function Notifications() {
  const [notifications, setNotifications] = useState([]);
  function handleDeleteNotification() {}

  useEffect(() => {
    const callback = eventManager.attach(NOTIFICATION_EVENT_NAME, (message) => {
      const id = new Date().getTime();
      setNotifications((lastNotifications) => [
        ...lastNotifications,
        { id, message }
      ]);
    });

    return () => eventManager.detach(NOTIFICATION_EVENT_NAME, callback);
  }, []);

  return (
    <ul>
      {notifications.map(({ id, message }) => (
        <li key={id}>
          {message}{" "}
          <button id={id} onClick={handleDeleteNotification}>
            close
          </button>
        </li>
      ))}
    </ul>
  );
}

function SomeChild1() {
  return (
    <div>
      SomeChild1
      <SomeChild2 />
    </div>
  );
}

function SomeChild2() {
  function tiggerNotificaiton() {
    eventManager.emit(
      NOTIFICATION_EVENT_NAME,
      `Notification ${new Date().getTime()}`
    );
  }

  return (
    <div>
      SomeChild2
      <button onClick={tiggerNotificaiton}>Notification trigger</button>
    </div>
  );
}


Demo is very simple. We are attaching the event where we need the data and for sending the data for attached observer, we are emitting the event.

Here we are using the notification.attach and when data will come we will update the state with the obtained data. And in another component we simply calling the eventManager.emit with the data.

At first glace it looks very ordinary implementation but it is very powerful when we need this kind of functionality in very complex application with improved performance.

Event Observer is no doubt is a very powerful in its own. but every power comes with some issues/disadvantages. If we don’t use Event Observer sparingly it will create a lot of issues. Another thing to notice is that It is not the solution for all kind of problems. As it is hard to test.

 

Here is the working Demo.

 

Leave a Reply

Your email address will not be published.