Eljan Simuratli
9/5/2025
Observer Pattern: From Class-Based Implementation to React Event Bus

The Observer pattern is also called the publish/subscribe pattern in short. It means that when a change happens, we notify all observers that have subscribed.
When we can use it:
- Event flow (button clicked, some information comes from the socket)
- Broadcasting changes from the store to components.
- If u have
loosely-coupledstructure
When we cannot use:
- Simple one-time interaction between two modules (can become overly complex)
- If chained notifications create performance/observability issues
I will give you 3 examples for usage of Observer pattern.
Class Based:
classbased
class Subject {
constructor() {
this.observers = new Set();
}
subscribe(fn) {
this.observers.add(fn);
return () => this.observers.delete(fn); // unsubscribe
}
notify(payload) {
for (const fn of this.observers) fn(payload);
}
}
// kullanım
const subject = new Subject();
const log1 = (x) => console.log("obs1:", x);
const log2 = (x) => console.log("obs2:", x);
const off1 = subject.subscribe(log1);
subject.subscribe(log2);
subject.notify("First Message");
off1(); // obs1 removed
subject.notify("Second Message");Step 1: We create observers with Set()Why we choose Set(); because it removes duplicates and the delete and add operations are faster than others. We store fn(listener function) in here.
Step 2: Creating a subscribe function, it also includes an unsubscribe function:
subscribe(fn) {
this.observers.add(fn);
return () => this.observers.delete(fn); // unsubscribe
}Step 3: The Notify function helps us to send the payload to all observers:
notify(payload) {
for (const fn of this.observers) fn(payload);
}Usage:
We call the Subject class for a new Set of functions:
const subject = new Subject();We create 2 log function for testing it:
const log1 = (x) => console.log("obs1:", x);
const log2 = (x) => console.log("obs2:", x);Now Set is look like : { log1, log2 }. When we run subject.notify(“ilk mesaj”) it run 2 log function inside set:
obs1: First Message
obs2: First MessageWhen we run off1() it only remove (unsubscribe) log1 from Setnow Setis {log2} only.
Minimal event bus:
export function createEventBus() {
const listeners = new Set();
const subscribe = (fn) => (listeners.add(fn), () => listeners.delete(fn));
const publish = (data) => listeners.forEach((fn) => fn(data));
return { subscribe, publish };
}
// usage
const bus = createEventBus();
const off = bus.subscribe((d) => console.log("got:", d));
bus.publish({ type: "LOGIN", user: "eljan" });
off();In React:
import React from "react";
const listeners = new Set();
const publish = (d) => listeners.forEach((fn) => fn(d));
const subscribe = (fn) => (listeners.add(fn), () => listeners.delete(fn));
function Publisher() {
return <button onClick={() => publish("butona tıklandı!")}>Publish</button>;
}
function Subscriber({ label }) {
const [msg, setMsg] = React.useState("-");
React.useEffect(() => subscribe(setMsg), []);
return <p>{label}: {msg}</p>;
}
export default function App() {
return (
<div>
<Publisher />
<Subscriber label="A" />
<Subscriber label="B" />
</div>
);
}You can get Other Patterns from my Github repo.