Loading article…
Loading article…
We use an everything-is-a-module approach, alongside micro-services everywhere in our company. This usually is a good place to be in. Yet sometimes you reach to a point where you component being a module is just too much.
In your website, you have some live tweet viewers. The tweet viewer auto updates, and has a specific server for data fetching. The server API endpoint returns all tweets together. Each tweet viewer is a standalone module, imported from and installed react library you hold.
Cool.
Now, you want to use multiple tweet viewers on screen, while keep calling just one fetch for all of them.
After all, events were invented for a reason. It helps us communicating between different aspects of the web application. User interaction events help us connect UI to logic and create behavior. So why don't we use this mechanism to connect our multiple UI components to data, to create one-way data binding?
Recipe:
import React, { useEffect, useState } from "react";
import { EventHandler } from "../logic/event-handler";
export default function TweetViewer({ tweetId }) {
const [tweetData, setTweetData] = useState({});
useEffect(() => {
const handler = (event) => {
setTweetData(event.detail);
};
EventHandler.subscribe(tweetId + "::data", handler);
// Cleanup if needed
return () => {
window.removeEventListener(tweetId + "::data", handler);
};
}, [tweetId]);
const { author, tweet } = tweetData;
return (
<div className="tweet">
<h4>author: @{author}</h4>
<p>tweet: {tweet}</p>
</div>
);
}
import React, { useEffect } from "react";
import ReactDOM from "react-dom";
import TweetViewer from "./components/TweetViewer";
import { EventRouter } from "./logic/event-handler";
function App() {
const tweetIds = [1, 2, 3, 4, 5];
useEffect(() => {
const router = new EventRouter();
router.fetchTweets(tweetIds);
}, []);
return (
<div className="App">
{tweetIds.map((id) => (
<TweetViewer tweetId={id} key={id} />
))}
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
class CustomEventHandler extends EventTarget {
subscribe(eventName, cb) {
window.addEventListener(eventName, cb);
}
emit(eventName, payload) {
window.dispatchEvent(new CustomEvent(eventName, { detail: payload }));
}
}
export const EventHandler = new CustomEventHandler();
export class EventRouter {
constructor() {
this.eventHandler = EventHandler;
}
fetchTweets(tweetIds) {
setInterval(() => {
tweetIds.forEach((id) => {
this.eventHandler.emit(id + "::data", {
author: randomString(10),
tweet: randomString(),
});
});
}, 3000);
}
}
// mimic response
function randomString(length) {
const strLength = length || Math.random() * 100;
return Math.random().toString(36).substring(strLength);
}