DEV Community

loading...

Publisher subscriber pattern in React.js

avinash8847 profile image Avinash ・3 min read

Last week I learnt a pattern called Publisher subscriber(Pub-Sub) pattern and tried to implement it in react. This can easily be done with React's context API or any third party library like Redux, but I took a different approach.

tl;dr: NOTE: This code is not production ready. Complete working app can be found here

What is Pub/Sub pattern?

According to Wikipedia

Pub-Sub is a messaging pattern where senders of messages, called publishers, do not program the messages to be sent directly to specific receivers, called subscribers, but instead categorize published messages into classes without knowledge of which subscribers, if any, there may be. Similarly, subscribers express interest in one or more classes and only receive messages that are of interest, without knowledge of which publishers, if any, there are.

In simple words, the publisher and subscriber are unaware of each other. All the communication between them is taken through events which are emitted from the publisher and notifies subscriber. As shown bellow

img


Pubsub pattern source msd blog

Implementing this in React

I have tried to use the Pub-Sub pattern to react in the most simplified manner that I could do with only two events (on and emit).

In this example, I'll be creating a simple modal which will act as a subscriber. Let's get started:

export const Modal = () => {
  const [content, setContent] = useState("no content");
  const [showModal, setShowModal] = useState(false);

  const setMessage = (message) => {
    setContent(message);
    setShowModal(true);
  };
  const clearMessage = () => {
    setContent("");
    setShowModal(false);
  };

  if (showModal) {
    return (
      <div>
        <h2>{content}</h2>
        <button onClick={clearMessage}>Close Modal </button>
      </div>
    );
  }
  return null;
};

Then I need an event bus which will be solely responsible for passing messages around. Here it is:


const event = {
  list: new Map(),
  on(eventType, eventAction) {
    this.list.has(eventType) || this.list.set(eventType, []);
    if (this.list.get(eventType)) this.list.get(eventType).push(eventAction);
    return this;
  },

  emit(eventType, ...args) {
    this.list.get(eventType) &&
      this.list.get(eventType).forEach((cb) => {
        cb(...args);
      });
  }
};

Here I have on event actions on and emit for a more complex system you can other event systems you can add other events such as clear, off etc.

Once we have our event system on its place let hook this event system in our modal

export const Modal = () => {
  const [content, setContent] = useState("no content");
  const [showModal, setShowModal] = useState(false);

  const setMessage = (message) => {
    setContent(message);
    setShowModal(true);
  };
  const clearMessage = () => {
    setContent("");
    setShowModal(false);
  };

  useEffect(() => {
    event.on("showModal", setMessage).on("clearAllMessage", clearMessage);
  }, []);
  if (showModal) {
    return (
      <Container>
        <h2>{content}</h2>
        <button onClick={clearMessage}>Close Modal </button>
      </Container>
    );
  }
  return null;

Next in order to complete the puzzle we need final pice that is Publisher which will emit the events


export const ModalPublisher = {
  message: (content) => {
    event.emit("showModal", content);
  },
  clearAllMessage: () => {
    event.emit("clearAllMessage");
  }
};

Okay once everything is on the place you can now we can use it in App.js


function App() {
  return (
    <div className="App">
      <Modal showModal={true} />
      <h1
        onClick={() => {
          ModalPublisher.message("this is the content from App");
        }}
      >
        Hello CodeSandbox
      </h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

Conclusion

This code is still not production-ready still there are many edge case to cover. I have used this pattern for Modal and toast.

Comments?

  • Do you use like this pattern?
  • What else this pattern can be used for?

Discussion

pic
Editor guide
Collapse
paras594 profile image
Paras

Nice try, I know this pattern but never got a proper use case to use it. Great you tried to use it with react !