How to Recreate React Features Using Only Vanilla JavaScript


As a web developer, understanding the underlying principles of popular libraries and frameworks can be incredibly valuable. In this article, we'll explore how to implement a basic React-like functionality using only vanilla JavaScript. By understanding the mechanics behind React, you'll gain a deeper appreciation for the design choices and capabilities of this powerful library.

React Fundamentals

Before diving into the implementation, let's quickly review the key concepts in React:

  1. Components: React allows you to build your application as a tree of reusable components, each responsible for rendering a piece of the UI.

  2. Props: Components receive data from their parent components through "props," which are essentially attributes passed down to the child components.

  3. State: Components can manage their own internal state, which can be updated to trigger re-renders and update the displayed UI.

  4. Virtual DOM: React uses a virtual DOM, a lightweight in-memory representation of the actual DOM. This allows it to efficiently update the real DOM by only making the necessary changes.

Vanilla JS Implementation

Now, let's see how we can implement a basic React-like functionality using plain JavaScript:

function renderReactElement(reactElement, mainContainer) {
  // Create a new DOM element based on the 'type' property of the reactElement
  const element = document.createElement(reactElement.type);

  // Set the innerHTML of the element to the 'children' property
  element.innerHTML = reactElement.children;

  // Iterate over the 'props' object and set the corresponding attributes or event listeners
  Object.entries(reactElement.props).forEach(([propName, propValue]) => {
    // If the prop name starts with 'on', assume it's an event listener and add it to the element
    if (propName.startsWith('on') && typeof propValue === 'function') {
      const eventName = propName.toLowerCase().substring(2);
      element.addEventListener(eventName, propValue);
    } else {
      // Otherwise, set the prop as an attribute on the element
      element.setAttribute(propName, propValue);
    }
  });

  // Append the created element to the main container
  mainContainer.appendChild(element);
}

// Create a React-like element
const reactElement = {
  type: 'button',
  props: {
    id: 'container',
    className: 'my-container',
    onClick: () => alert('Welcome :)'),
  },
  children: 'Click On ME',
};

// Get the main container element
const mainContainer = document.getElementById('root');

// Render the React-like element to the main container
renderReactElement(reactElement, mainContainer);

The key steps in this implementation are:

  1. Create a new DOM element based on the type property of the reactElement object.

  2. Set the innerHTML of the element to the children property.

  3. Iterate over the props object and set the corresponding attributes or event listeners on the element.

  4. Append the created element to the mainContainer.

This vanilla JavaScript approach allows you to create and render a basic React-like element without the use of any libraries or frameworks. However, it lacks some of the advanced features and capabilities that React provides, such as the virtual DOM, efficient updates, and state management.

Comparison to React

Let's compare the vanilla JavaScript implementation to how React would handle the same functionality:

React Approach:

const MyButton = ({ onClick, children }) => {
  return (
    <button id="container" className="my-container" onClick={onClick}>
      {children}
    </button>
  );
};

const App = () => {
  const handleClick = () => {
    alert('Welcome :)');
  };

  return (
    <div id="root">
      <MyButton onClick={handleClick}>Click On ME</MyButton>
    </div>
  );
};

The key differences between the two approaches are:

  1. Component Encapsulation: In React, the button logic is encapsulated within the MyButton component, making it more reusable and easier to manage.

  2. Declarative Syntax: React's JSX syntax allows you to declare the UI structure more declaratively, without having to manually create and manipulate DOM elements.

  3. Virtual DOM: React uses a virtual DOM, which allows it to efficiently update the actual DOM by only making the necessary changes, unlike the direct DOM manipulation in the vanilla JavaScript approach.

  4. State Management: React provides built-in state management capabilities, making it easier to handle dynamic updates and interactions within your application.

  5. Lifecycle Methods: React components have access to lifecycle methods, which allow you to perform actions at different stages of the component's lifecycle, such as when it's mounted, updated, or unmounted.

Overall, the React approach is more concise, declarative, and easier to manage, especially as the complexity of the application grows. The vanilla JavaScript implementation, while functional, requires more manual DOM manipulation and doesn't provide the same level of abstraction and tooling that React offers.

Conclusion

In this article, we've explored how to implement a basic React-like functionality using vanilla JavaScript. By understanding the underlying mechanics of React, you'll gain a deeper appreciation for the design choices and capabilities of this powerful library.

While the vanilla JavaScript approach demonstrates the core concepts, it lacks the advanced features and tooling that React provides. As you continue your web development journey, exploring and understanding the internals of popular frameworks and libraries can help you make more informed decisions and better understand the trade-offs between different approaches.

2
Subscribe to my newsletter

Read articles from Naitik Prajapati directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Naitik Prajapati
Naitik Prajapati

I am a dedicated Full-Stack Developer with a strong passion for creating innovative web applications. Proficient in both front-end and back-end technologies, I excel in HTML, CSS, JavaScript, React.js, and Node.js, and have experience with database management systems like PostgreSQL and MySQL. As a third-year student, I combine technical expertise with a keen understanding of user experience to build robust, user-friendly applications.