Skip to main content

EventManager

Dependencies​

This library does not require any dependencies.

Usage​

import EventManager from "@arclockproject/common/library/EventManager";

const events = new EventManager<{
pay: (client: string, details: { amount: number }) => void;
}>();

const onPay = (client: string, details: { amount: number }) => {
console.log(`${client} paid ${details.amount}`);
};

events.addListener("pay", onPay);
events.dispatch("pay", "Alex", { amount: 100 }); // "Alex paid 100"
events.removeListener("pay", onPay);

removeListener only works with the same function you passed to addListener, so keep a reference to it. addListener returns the passed function to make that easier.

The examples below are live. Edit the code to try your own values.

Sending and listening​

One EventManager shared by a sender and a few listeners. Each listener subscribes when it appears and unsubscribes when it is removed, so hidden listeners stop getting messages.

▶ Show code (click to view and edit)
Live Editor
function Chat() {
  const events = useMemo(() => new EventManager(), []);
  const [text, setText] = useState("Hello!");
  const [shown, setShown] = useState({ Alex: true, Sam: true, Kim: false });

  return (
    <div>
      <input value={text} onChange={(e) => setText(e.target.value)} />{" "}
      <button onClick={() => events.dispatch("message", "You", text)}>Send</button>
      <p>
        {Object.keys(shown).map((name) => (
          <label key={name} style={{ marginRight: 12 }}>
            <input
              type="checkbox"
              checked={shown[name]}
              onChange={() => setShown({ ...shown, [name]: !shown[name] })}
            />{" "}
            {name}
          </label>
        ))}
      </p>
      <div style={{ display: "flex", gap: 8 }}>
        {Object.keys(shown).map((name) => shown[name] && <Inbox key={name} name={name} events={events} />)}
      </div>
    </div>
  );
}

function Inbox({ name, events }) {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const onMessage = events.addListener("message", (from, text) => {
      setMessages((list) => [...list, `${from}: ${text}`]);
    });
    return () => events.removeListener("message", onMessage);
  }, [events]);

  return (
    <div style={{ border: "1px solid var(--ifm-color-emphasis-300)", padding: 8, minWidth: 120 }}>
      <strong>{name}</strong>
      {messages.map((message, i) => (
        <div key={i}>{message}</div>
      ))}
    </div>
  );
}

render(<Chat />);
Result
Loading...

Adding a listener in one line​

addListener returns the function you pass in, and infers its parameter types from the event map. So you can write the listener inline and still keep a reference for removeListener. This is the Usage example made shorter:

import EventManager from "@arclockproject/common/library/EventManager";

const events = new EventManager<{
pay: (client: string, details: { amount: number }) => void;
}>();

const onPay = events.addListener("pay", (client, details) => { // Types are infered.
console.log(`${client} paid ${details.amount}`);
});
events.dispatch("pay", "Alex", { amount: 100 }); // "Alex paid 100"
events.removeListener("pay", onPay); // Removes the function made in events.addListener

API​

See the EventManager API reference for every prop, method and type.