Published on

Building a React Application with MVVM Pattern

Building a React Application with MVVM Pattern

Building a React Application with MVVM Pattern

Building a React Application with Custom EventEmitter and MVVM Pattern

In this article, we'll dive into a powerful architectural pattern that combines modern JavaScript, React, and a custom implementation of an EventEmitter to manage state changes in a reactive and efficient manner. This approach leverages the Model-View-ViewModel (MVVM) pattern, traditionally used in frameworks like Angular and Vue, and adapts it to React applications. The MVVM pattern can enhance your project's structure by providing a clear separation of concerns, which simplifies management and improves scalability.

Setting Up the Environment

To begin, ensure Node.js is installed on your machine. We'll start a new React project using Create React App:

npx create-react-app react-mvvm-demo
cd react-mvvm-demo
npm start

Creating a Custom EventEmitter Class

Our first step is to create a custom EventEmitter class. This class will manage event subscriptions and notifications:

class EventEmitter {
  constructor() {
    this.events = {};
  }

  on(eventName, listener) {
    if (!this.events[eventName]) {
      this.events[eventName] = [];
    }
    this.events[eventName].push(listener);
  }

  off(eventName, listener) {
    if (!this.events[eventName]) {
      return;
    }
    this.events[eventName] = this.events[eventName].filter(
      (l) => l !== listener
    );
  }

  emit(eventName, data) {
    const event = this.events[eventName];
    if (event) {
      event.forEach((listener) => {
        listener(data);
      });
    }
  }
}

Extending UserModel with EventEmitter

Next, we extend this EventEmitter to create a UserModel class. This model will manage user data and emit updates whenever the user list changes:

class UserModel extends EventEmitter {
  constructor() {
    super();
    this.users = [];
  }

  addUser(user) {
    this.users.push(user);
    this.emit("update", this.users);
  }

  getUsers() {
    return this.users;
  }
}

Building the React Component

We then build a React component that listens to updates from our UserModel:

import React, { useEffect, useState } from "react";

const userModel = new UserModel();

const UserComponent = () => {
  const [users, setUsers] = useState(userModel.getUsers());

  useEffect(() => {
    const handleUpdate = (newUsers) => {
      setUsers([...newUsers]);
    };

    userModel.on("update", handleUpdate);

    return () => userModel.off("update", handleUpdate);
  }, []);

  const handleAddUser = () => {
    userModel.addUser({ name: "New User", id: Date.now() });
  };

  return (
    <div>
      <button onClick={handleAddUser}>Add User</button>
      <ul>
        {users.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
};

export default UserComponent;

Integration into App.js

Finally, we integrate our UserComponent into the main App component to display it within our application:

import React from "react";
import UserComponent from "./UserComponent";

function App() {
  return (
    <div className="App">
      <h1>User Management</h1>
      <UserComponent />
    </div>
  );
}

export default App;

Conclusion

In this blog, we've successfully implemented an MVVM pattern in a React application using a custom EventEmitter class. This architecture not only provides a clean separation between the application's logical and presentation layers but also enhances the scalability and maintainability of the code. By creating a decoupled system where components communicate through events, developers can build more flexible and manageable applications.

Potential Use Cases

  1. Real-time Applications: Ideal for scenarios requiring real-time updates like chat apps or live sports scores.
  2. Complex UIs: Simplifies communication in complex interfaces, avoiding prop drilling and callback chains.
  3. Server-side Events: Can integrate seamlessly with server-side push technologies like WebSockets for reactive updates.

Using a custom EventEmitter with React opens up a realm of possibilities for crafting sophisticated web applications that are both robust and easy to maintain.