Understanding reconciliation in JS

Understanding reconciliation in JS

So here we will see how we create our own reconciler in javascript.

Creating our own Reconciler:

Let's set the stage for our scenario—analogous to the process of working on a todo app. Imagine we're striving to retrieve all the todos from our database, aiming to fetch and seamlessly display them within our HTML. To recreate this situation, we'll employ a similar concept: an orchestrated setInterval. This interval, occurring every 5 seconds, simulates the act of acquiring an updated array of todos—mirroring what we might do when fetching new data from a database.

Now that we've established the context, let's dive into the task at hand:

We've secured the data array, freshly plucked from our database through fetching.

Now the approach is as follows:

  1. We perform DOM manipulation, i.e., we take the div within which all the todos will be displayed(currently have the old data) and we will create an array of the HTML document that we receive from it.

  2. Now coming to the next step we iterate over this array 'data' checking if the todo of the same id is already in our dom.

    • if we have; then it is updated

    • if not it is created (added)

  3. After that, we check in the current children if their elements are in the received data or not:

    • if not we delete that element from currentChildren.

    • if yes then we just keep it.

// Function to create or update DOM elements based on provided data
function createDomElements(data) {
    // Get the parent element where the items will be displayed
    var parentElement = document.getElementById("mainArea");

    // Get the current children of the parent element and convert them to an array
    var currentChildren = Array.from(parentElement.children);

    // Process each item in the data array
    data.forEach(function (item) {
        // Check if a child with this ID already exists in the current DOM
        var existingChild = currentChildren.find(function (child) {
            return child.dataset.id === String(item.id);
        });

        if (existingChild) {
            // If it exists, update its content
            existingChild.children[0].innerHTML = item.title;
            existingChild.children[1].innerHTML = item.description;

            // Remove the existing child from the currentChildren array
            currentChildren = currentChildren.filter(function (child) {
                return child !== existingChild;
            });
        } else {
            // If it doesn't exist, create a new DOM element
            var childElement = document.createElement("div");
            childElement.dataset.id = item.id; // Store the item's ID on the element for future reference

            var grandChildElement1 = document.createElement("span");
            grandChildElement1.innerHTML = item.title;

            var grandChildElement2 = document.createElement("span");
            grandChildElement2.innerHTML = item.description;

            var grandChildElement3 = document.createElement("button");
            grandChildElement3.innerHTML = "Delete";
            grandChildElement3.setAttribute("onclick", "deleteTodo(" + item.id + ")"); // Assign the delete function with the item's ID

            // Append the child elements to the parent element
            childElement.appendChild(grandChildElement1);
            childElement.appendChild(grandChildElement2);
            childElement.appendChild(grandChildElement3);
            parentElement.appendChild(childElement);
        }
    });

    // Any children left in the currentChildren array no longer exist in the data, so remove them
    currentChildren.forEach(function (child) {
        parentElement.removeChild(child);
    });
}

// Automatically update the UI with random data every 2 seconds
window.setInterval(() => {
    let todos = [];
    for (let i = 0; i < Math.floor(Math.random() * 100); i++) {
        todos.push({
            title: "Go to the gym",
            description: "Exercise for 30 minutes",
            id: i + 1,
        });
    }

    // Call the function to create or update DOM elements based on the random data
    createDomElements(todos);
}, 5000);

More Optimisations:

In our pursuit of code optimization, we're not just settling for the ordinary. Our mission involves two crucial techniques: leveraging the virtual DOM and embracing the concept of batching.

Since direct updation in browser dom is an expensive task and we are in pursuit of optimizing the code so we perform two important tasks

  1. Virtual DOM Mastery: Instead of directly altering the browser DOM, we leverage the virtual DOM. This intermediary layer intelligently tracks changes, ensuring only necessary updates hit the browser. Performance bottlenecks? Not anymore.

  2. Batching Brilliance: We step into the world of batching. Rather than individual updates, we group them smartly. By executing batches in one sweep, we streamline efficiency and cut down on DOM interactions.

let vDOM = []; // Our initial vDOM is an empty array

// Function to create or update DOM elements based on vDOM data
function createDomElements() {
  var parentElement = document.getElementById("mainArea");

  var currentChildren = Array.from(parentElement.children);

  // Now, we'll compare our new vDOM to our actual DOM
  vDOM.forEach(function(item) {
    // Check if a child with this ID already exists in the DOM
    var existingChild = currentChildren.find(function(child) {
      return child.dataset.id === String(item.id);
    });

    if (existingChild) {
      // If it exists, update its content
      existingChild.children[0].innerHTML = item.title;
      existingChild.children[1].innerHTML = item.description;
      // Remove it from the currentChildren array
      currentChildren = currentChildren.filter(function(child) {
        return child !== existingChild;
      });
    } else {
      // If it doesn't exist in the DOM, create it
      var childElement = document.createElement("div");
      childElement.dataset.id = item.id; // Store the ID on the element for future lookups

      var grandChildElement1 = document.createElement("span");
      grandChildElement1.innerHTML = item.title;

      var grandChildElement2 = document.createElement("span");
      grandChildElement2.innerHTML = item.description;

      var grandChildElement3 = document.createElement("button");
      grandChildElement3.innerHTML = "Delete";
      grandChildElement3.setAttribute("onclick", "deleteTodo(" + item.id + ")");

      childElement.appendChild(grandChildElement1);
      childElement.appendChild(grandChildElement2);
      childElement.appendChild(grandChildElement3);
      parentElement.appendChild(childElement);
    }
  });

  // Any children left in the currentChildren array no longer exist in the data, so remove them
  currentChildren.forEach(function(child) {
    parentElement.removeChild(child);
  });

}

// Function to update the vDOM with new data
function updateVirtualDom(data) {
  vDOM = data.map(item => {
    return {
      id: item.id,
      title: item.title,
      description: item.description
    };
  });
}

// Simulate data updates every 5 seconds
window.setInterval(() => {
  let todos = [];
  for (let i = 0; i<Math.floor(Math.random() * 100); i++) {
    todos.push({
      title: "Go to gym",
      description: "Go to gym from 5",
      id: i+1
    });
  }

  updateVirtualDom(todos);
}, 5000);

// Simulate DOM updates every 1 second
window.setInterval(() => {
  createDomElements();
}, 1000);
  1. Introduction of vDOM: We introduced a new array called vDOM to store a virtual representation of our DOM elements. This virtual DOM acts as a middleman between our actual DOM and the data updates. The updateVirtualDom function now maps the incoming data to this virtual structure.

  2. Smarter DOM Manipulation: Instead of directly manipulating the actual DOM on every data change, we use the createDomElements function to work with the virtual DOM (vDOM). This function calculates changes between the virtual DOM and the actual DOM, only applying necessary updates, additions, and deletions. This technique significantly reduces the strain on the browser and enhances efficiency.

  3. Batching Updates: By simulating data updates and DOM updates at separate intervals, we've effectively introduced batching. This means we're bundling multiple changes into one update cycle, reducing the overhead of frequent interactions with the actual DOM. As a result, our app operates more smoothly and resourcefully.

With these optimizations in place, our code gracefully embraces the virtual DOM and batching. The outcome? A highly responsive and efficient application that seamlessly manages data and interactions with finesse. Our journey through code optimization concludes with these ingenious strategies shaping a remarkable user experience.


🚀 Embarking on our journey into React's inner workings, we've uncovered the hidden magic that propels seamless user experiences. Through the art of the virtual DOM and the rhythm of batching, React orchestrates a symphony of optimization. The virtual DOM's dance with the actual DOM brings efficiency, while batching's choreography minimizes unnecessary interactions. As we close this chapter, 🌟 remember that these are mere glimpses into the intricate dance of React's optimizations. Stay curious and keep exploring—the world of tech holds endless wonders to unveil.

⚡️ With virtual DOM finesse and batching brilliance, React weaves a tale of streamlined code and enhanced performance. Reconciliation's magic unfolds as updates, creations, and deletions synchronize in harmony. These optimization techniques are but a glimpse into the intricate symphony that React conducts behind the scenes. As we conclude this chapter, 🌈 let's cherish the insight gained and eagerly embrace the continuous journey of discovery in the realm of technology.