January 29, 2023 Event Based Architecture Using React Disclaimer: This blog was originally posted on Medium. Introduction There are plenty of ways to architect your React project such as by using third party state management libraries Redux, Mobx etc or we can use simply a context API. In additon, we can also decide on using pure component based architecture i.e. all of the logic is dealt by component. We can also use React Hooks based architecture, presentation-container design pattern etc etc. About This Blog However, today I am going to implement Event Based Architecture where `components`, `hooks` etc and other layers of the architecture can interact with each other via `EventBus` layer. This `EventBus` is designed based on `pub/sub` design pattern which is a famous design pattern in which `publisher` has several unknown `subscribers` and it broadcasts an event and all the subscribers are listening to that event and they do operations based on the `dispatched` event. Moreover, our solution will be minimalistic without taking consideration of high level of issues that needs to be solved. This architecture should become a good starting point for your next project adventure(). In this architecture, a component `dispatches` an event and the target component listens to that event and calls a callback function when it receives that event. Below is the diagram of the architecture we are going to design and implement in this article. About Project It would be great idea if we get into the practical project to demonstrate how can we can achieve such an architecture. For this, I decided to create a sample project in which we are going to enter contacts details and save it in a component state. It has two components(nothing fancy) i.e. `Home` and `Form`. These two components interact with each other via `EventBus`. Thus it allows us to keep them loosely coupled with each other. Before we get into this article if you would like to see the code you can find it on my Github profile here. Let’s Begin Coding Let’s run below command in your terminal to create a react project. npx create-react-app react-event-architecture Give it a few minutes. It will create a folder with the name of `react-event-architecture`. Simply `cd` into that folder: cd react-event-architecture Now open this folder in your favourite editor/IDE. What I will do is first I will implement the basic application with both pages and later start working on `EventBus` layer. First of all create a new folder with the name of `components` inside `src` folder then create two more folders i.e. `Home` and `Form`. Inside `Home` create `Home.js` file and inside `Form` folder create `Form.js` file. Now open `Form.js` in your favorite editor and paste below code in it. export const Form = () => { const save = (event) => { event.preventDefault(); } return ( <> <form onSubmit={save}> <label> Name: <input type="text" name="name" /> </label> <label> Email: <input type="email" name="email" /> </label> <label> Number: <input type="number" name="phone" /> </label> <button>Save</button> </form> </> ); } In this component, I am simply creating an HTML form with `name`, `email` and `Number` fields and when user clicks on `save` button the it calls `save` function. I will implement this function a little later in this article. Now open `Home.js` file and paste below code in it. import { useEffect, useState } from 'react'; export const Home = () => { const [contacts, setContacts] = useState([]); return ( <> {contacts.length <= 0 && `No contacts found`} </> ); } As you can see it is also a very simple component for now. I am importing `useEffect` and `useState` hooks. Then inside component, I am creating a local state variable `contacts`. Inside JSX, I am checking if there are any contacts in local state variable `contacts` if there are nothing show a message `No contacts found`. Now open `App.js` file and paste below code. import { Home } from './components/Home/Home'; import { Form } from './components/Form/Form'; function App() { return ( <div> <Form /> <Home /> </div> ); } export default App; Here I am importing both `Home` and `Form` components and rendering them inside App component. Now that we are done with the basic stuff, it is time to get into `EventBus` which is the gest of this article. Create `EventBus` folder inside `src` folder and create a file inside that folder with the name of `EventBus.js`. Now open that file and paste below code in it. export const EventBus = () => { let eventsList = {}; return () => { const listen = (eventName, callback) => { if (!eventsList[eventName]) { eventsList[eventName] = []; } eventsList[eventName].push(callback); }; const dispatch = (eventName, dataObject) => { if (!eventsList[eventName] || eventsList[eventName].length < 1) { return; } eventsList[eventName].forEach((listener) => { listener(dataObject || {}); }); }; const destroy = (eventName) => { delete eventsList[eventName]; }; return { dispatch, listen, destroy, }; }; }; As you can see, I am exporting an `EventBus` function and inside this function I am declaring `eventLists` map. Then I am returning anonymous function and inside that I have three functions i.e. `dispatch`, `listen` and `destroy`. `dispatch` function is used to trigger an event and `listen` function is used to listen to the event that is triggered via `dispatch event. The destroy function simply gets rid of the event. Now that we have the `EventBus` layer setup. It is time to use it. Lets open `App.js` file and update your code with below code. import { Home } from './components/Home/Home'; import { Form } from './components/Form/Form'; import { EventBus } from './EventBus/EventBus'; // New Line window.Events = EventBus()(); // New Line function App() { return ( <div> <Form /> <Home /> </div> ); } export default App; Lines number 3 and 5 and new lines added to this file. I am importing `EventBus` and the adding `Events` property to global scope assigning `EventBus()()` function by calling it. After doing that all the functions inside `EventBus` is available in entire project. Now what we want is that when user enters details for new contact and press `save` button we will `dispatch` an event. So lets open `Form.js` file and update it with below code. export const Form = () => { const save = (event) => { event.preventDefault(); const name = event.target.name.value; // New Line const email = event.target.email.value; // New Line const phone = event.target.phone.value; // New Line window.Events.dispatch('SAVE', { name, email, phone }); // New Line } return ( <> <form onSubmit={save}> <label> Name: <input type="text" name="name" /> </label> <label> Email: <input type="email" name="email" /> </label> <label> Number: <input type="number" name="phone" /> </label> <button>Save</button> </form> </> ); } Lines 5,6,7 and 9 and new lines added to this file. I am getting `name`, `phone` and `email` from the form upon `save` button clicked and the I dispatch `SAVE` event by calling `window.Events.dispatch(‘SAVE’, { name, email, phone });`. Now I am going to listen to that event in `Home.js` component where all list of contacts and saved in a local variable state and displays all these contacts in the form of table. Open `Home.js` component and paste below code. import { useEffect, useState } from 'react'; export const Home = () => { const [contacts, setContacts] = useState([]); useEffect(() => { window.Events.listen('SAVE', saveContact); }, []); const saveContact = ({ name, email, phone }) => { setContacts((prev) => [...prev, { name, email, phone }]); } console.log({ contacts }); return ( <> {contacts.length <= 0 && `No contacts found`} { contacts.length > 0 && <table> <thead> <tr> <th>Name</th> <th>Email</th> <th>Phone</th> </tr> </thead> <tbody> { contacts.map((contact, index) => ( <tr key={index}> <td>{contact.name}</td> <td>{contact.email}</td> <td>{contact.phone}</td> </tr> )) } </tbody> </table> } </> ); } Inside the `useEffect` hook I am listening to `SAVE` event and then call the callback function save contact and pass the object received from the `dispatch` function from `Form.js` component. Inside `saveContact` function I am updating the `contacts` state with newly added contact from the `Form.js` component. Inside JSX, I added a table and renders all the contacts there which is self explanatory. Conclusion Now that you can add a new contact via Event Base architecture, the next steps would be to implement RUD(Read, Update, Delete) in CRUD concept. I will leave that to you so that you can implement it on your own. This is the basic form on implementing front end architecture in React and will give you more ideas to find the best possible approach on how this could be used in your applications with different variant of this solution. Moreover, after completing the building of your application you would also need to add a database to store your information. Therefore, I write an article on AWS DynamoDB & Nextjs Building Real World App. Architecture front end development Frontend Architecture Reactjs architecturefrontendreactreactjs