Components, Elements and Instances

Learn the fundamental concepts of React: how components, elements, and instances work together to build modern web applications.

React Components, Elements and Instances

The goal of this blog and subsequent posts is to introduce React, foster knowledge, solidify ideas, concepts and fundamentals about technologies. I will cover topics from basic to advanced, trying to be thorough. I express my gratitude to the readers in advance. So let’s dive into React - we need to understand its operation and architecture.

Definition and History

React is a JavaScript library created by Facebook (Meta) around 2013 and has since been gaining popularity while reducing jQuery usage (2006). In the jQuery era, it came with the proposal to facilitate DOM manipulation, providing queries to directly access the DOM. This was better than using selectors like document.getElementById, getElementsByClassName and others directly.

Over the years, jQuery became heavy and complex due to the scale and complexity of web projects. Libraries like React, Vue, Angular and experiments like hell.js started to emerge and were gradually adopted. React became popular and today is one of the most used libraries.

Principle

Components, Elements and Instances are fundamental concepts for understanding how React operates.

On react, components are functions or classes that return React elements, are reusable and contain behavior logic, UI and state. React comes with the premise of composing the UI through components, reusing these components and managing state linked to the UI in these components. In its primary form, React is a library that transforms components into elements and these into instances that are rendered in the DOM. We can think of components as architectural plans, elements as technical drawings, and instances as physical construction.

React Component

React Element

DOM Instance

function Button() {
  return <button>Click</button>;
}

// Or class
class Button extends React.Component {
  render() {
    return <button>Click</button>;
  }
}
const element = <Button />;

// Represents:
{
  type: Button,
  props: {},
  key: null,
  ref: null
}
ReactDOM.render(
  element, 
  container
);

// Result in DOM:
<button>Click</button>

Evolution of DOM Selectors

To better understand the value React brings, let’s compare how the same functionality evolved over time:


// Selecting elements
const button = document.getElementById('myButton');
const items = document.getElementsByClassName('item');
const links = document.querySelectorAll('a.external');

// Click event directly on DOM
button.addEventListener('click', function() {
  const newDiv = document.createElement('div');
  newDiv.className = 'message';
  newDiv.textContent = 'Clicked!';
  document.body.appendChild(newDiv);
});

// Updating DOM elements
const list = document.getElementById('todoList');
list.innerHTML = '';
todos.forEach(function(todo) {
  const li = document.createElement('li');
  li.textContent = todo.text;
  if (todo.completed) {
    li.className = 'completed';
  }
  list.appendChild(li);
});

// Selecting elements
const $button = $('#myButton');
const $items = $('.item');
const $links = $('a.external');

// Click event directly on DOM
$button.on('click', function() {
  $('<div>')
    .addClass('message')
    .text('Clicked!')
    .appendTo('body');
});

// Updating DOM elements
const $list = $('#todoList');
$list.empty();
$.each(todos, function(index, todo) {
  $('<li>')
    .text(todo.text)
    .toggleClass('completed', todo.completed)
    .appendTo($list);
});

// React Component
function TodoList({ todos }) {
  const [message, setMessage] = useState('');

  const handleClick = () => {
    setMessage('Clicked!');
  };

  return (
    <div>
      <button onClick={handleClick}>
        Click here
      </button>
      
      {message && <div className="message">{message}</div>}
      
      <ul>
        {todos.map(todo => (
          <li 
            key={todo.id}
            className={todo.completed ? 'completed' : ''}
          >
            {todo.text}
          </li>
        ))}
      </ul>
    </div>
  );
}

Performance and Optimization

One of the main benefits of React is performance optimization through the Virtual DOM. Let’s understand the performance differences between approaches:

Direct DOM Manipulation

// ❌ Problem: Multiple costly DOM operations
function updateList(items) {
  const list = document.getElementById('list');
  
  // Each operation causes reflow/repaint
  list.innerHTML = ''; // Reflow 1
  
  items.forEach(item => {
    const li = document.createElement('li'); // Reflow 2
    li.textContent = item.name;
    list.appendChild(li); // Reflow 3, 4, 5...
  });
}

// Result: 1 + N reflows (very expensive!)

Problems:

  • Each DOM change causes reflow/repaint
  • No automatic optimization
  • Difficult to optimize manually
  • Performance degrades with scale

jQuery - Better, but still limited

// ⚠️ Better, but still manipulates DOM directly
function updateList(items) {
  const $list = $('#list');
  
  // Reduces reflows using fragment
  $list.empty(); // Reflow 1
  
  const fragment = $(document.createDocumentFragment());
  
  items.forEach(item => {
    fragment.append(
      $('<li>').text(item.name)
    );
  });
  
  $list.append(fragment); // Reflow 2
}

// Result: 2 reflows (better, but manual)

Problems:

  • Requires manual optimization (fragments)
  • Still manipulates DOM directly
  • No intelligent comparison
  • Developers often forget to optimize

React - Virtual DOM and Reconciliation

// ✅ React optimizes automatically
function List({ items }) {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

// React internally:
// 1. Creates Virtual DOM (pure JavaScript)
// 2. Compares with previous Virtual DOM (diff)
// 3. Calculates minimal necessary changes
// 4. Applies only changes to real DOM
// 5. Groups operations (batching)

Advantages:

  • ✅ Virtual DOM: Operations in JavaScript (fast)
  • ✅ Diffing: Calculates minimal changes
  • ✅ Batching: Groups updates
  • ✅ Reconciliation: Reuses existing elements
  • ✅ Automatic: No extra code

How Virtual DOM Works

The Virtual DOM is a lightweight representation of the DOM tree kept in memory. It’s essentially a JavaScript object that mirrors the structure of the real DOM, but without the computational costs of direct manipulation.

Update Cycle:

  1. Initial Render: React creates the Virtual DOM and corresponding real DOM
  2. State Change: When state changes, React creates a new Virtual DOM
  3. Diffing (Reconciliation): React compares the new Virtual DOM with the previous one
  4. Change Calculation: Identifies exactly which nodes changed
  5. Commit: Applies only necessary changes to the real DOM in batch

1. Initial State

2. New State

3. Minimal Update

// Virtual DOM
{
  type: 'ul',
  children: [
    { type: 'li', text: 'Item 1' },
    { type: 'li', text: 'Item 2' }
  ]
}

// Real DOM
<ul>
  <li>Item 1</li>
  <li>Item 2</li>
</ul>
// Updated Virtual DOM
{
  type: 'ul',
  children: [
    { type: 'li', text: 'Item 1' },
    { type: 'li', text: 'Item 2' },
    { type: 'li', text: 'Item 3' }
  ]
}

// React does diff
// Detects: +1 new item
// React applies only:
ul.appendChild(
  createElement('li', 'Item 3')
);

// Final Real DOM
<ul>
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li> ← only this changed
</ul>

Why is the Virtual DOM efficient?

  • In-Memory Operations: Manipulating JavaScript objects is much faster than manipulating the DOM
  • Batching: React groups multiple changes into a single DOM update
  • Optimized Diff Algorithm: O(n) complexity instead of O(n³) of traditional tree diff algorithms
  • Smart Heuristics:
    • Elements of different types generate completely different trees
    • Elements with keys allow consistent identification between renders
    • Property changes don’t recreate the element, just update attributes

When Each Approach Makes Sense

Vanilla JavaScript:

  • ✅ Very simple projects
  • ✅ Critical performance in isolated operations
  • ✅ No dependencies
  • ❌ Complex apps with state

jQuery:

  • ✅ Legacy projects
  • ✅ Simple DOM manipulations
  • ✅ Compatibility with old browsers
  • ❌ Modern SPAs

React:

  • ✅ Single Page Applications (SPAs)
  • ✅ Complex interfaces with lots of state
  • ✅ Performance at scale
  • ✅ Maintainability and componentization
  • ❌ Simple static sites (overkill)
← Back to Blog