Skip to main content

🎨 What is Virtual DOM?

πŸ“– Definition​

Virtual DOM is a lightweight copy of the real DOM, a JavaScript object that exists only in memory. Libraries like React use the Virtual DOM to handle UI changes virtually first, then apply only minimal changes to the real DOM. This significantly improves the performance of web applications.

🎯 Understanding Through Analogies​

Building Blueprint Analogy​

Comparing Virtual DOM to building blueprints:

Real Building (Real DOM)
β”œβ”€ Expensive construction costs
β”œβ”€ Takes long time to modify
└─ Hard to demolish and rebuild

Blueprint (Virtual DOM)
β”œβ”€ Freely modify on paper
β”œβ”€ Fast and cheap
β”œβ”€ Can compare multiple options
└─ Only final version applied to real building

Process:
1. Multiple modifications on blueprint (Virtual DOM)
2. Identify only changed parts
3. Apply minimum changes to real building (Real DOM)

This saves construction costs and time!

Word Processor Analogy​

Notepad (Vanilla JavaScript)
- Saves immediately with every keystroke
- Slow and inefficient
- Repeated file I/O

Word Processor (React + Virtual DOM)
- Edit in memory
- Track changes
- Write to file only when save button is pressed
- Fast and efficient

Virtual DOM = Memory buffer
Real DOM = Saved to disk

Game Rendering Analogy​

In game development:

Drawing directly to screen (Direct Real DOM manipulation)
- Flickering occurs
- Performance degradation
- Difficult to partially update

Double Buffering (Virtual DOM approach)
1. Draw on back screen (buffer)
2. Swap with front screen when complete
3. Smooth rendering
4. Update only changed pixels

Virtual DOM plays the same role as double buffering!

βš™οΈ How It Works​

1. What is DOM?​

// DOM (Document Object Model)
// Interface that allows manipulating HTML with JavaScript

<!DOCTYPE html>
<html>
<body>
<div id="root">
<h1>Hello</h1>
<p>World</p>
</div>
</body>
</html>

// DOM Tree
document
└─ html
└─ body
└─ div#root
β”œβ”€ h1 ("Hello")
└─ p ("World")

// Manipulating DOM with JavaScript
document.getElementById('root').innerHTML = '<h1>Changed!</h1>';

2. Problems with Real DOM​

// ❌ Inefficient Real DOM manipulation

// Problem 1: Full re-rendering every time
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
div.textContent = `Item ${i}`;
document.body.appendChild(div); // 1000 DOM manipulations!
}

// Problem 2: Layout recalculation (Reflow)
element.style.width = '100px'; // Reflow occurs
element.style.height = '100px'; // Reflow occurs
element.style.margin = '10px'; // Reflow occurs

// Problem 3: Complex UI updates
// Entire component must be recreated even if one character changes
<div>
<Header />
<Content /> ← Only this changed
<Footer />
</div>
// Must recreate everything!

// Why Real DOM manipulation is slow:
// 1. Communication between browser rendering engine and JavaScript engine
// 2. Reflow (layout recalculation)
// 3. Repaint (redrawing screen)

3. Virtual DOM Operation Process​

// βœ… Efficient handling with Virtual DOM

// Step 1: Initial rendering
const initialVirtualDOM = {
type: 'div',
props: { id: 'app' },
children: [
{ type: 'h1', props: {}, children: ['Count: 0'] },
{ type: 'button', props: {}, children: ['Click'] }
]
};

// Reflected in Real DOM
<div id="app">
<h1>Count: 0</h1>
<button>Click</button>
</div>

// Step 2: State change (count = 1)
const newVirtualDOM = {
type: 'div',
props: { id: 'app' },
children: [
{ type: 'h1', props: {}, children: ['Count: 1'] }, // ← Changed
{ type: 'button', props: {}, children: ['Click'] }
]
};

// Step 3: Diffing (calculate differences)
const diff = {
path: ['div', 'h1', 'text'],
oldValue: 'Count: 0',
newValue: 'Count: 1'
};

// Step 4: Reconciliation
// Apply only minimal changes to Real DOM
document.querySelector('h1').textContent = 'Count: 1';
// div and button remain unchanged!

4. Diffing Algorithm​

// React's Diffing Algorithm Principles

// Rule 1: Different element types create different trees
// Before
<div>
<Counter />
</div>

// After
<span>
<Counter />
</span>

// Result: Complete replacement of div with span
// Counter also unmounted and remounted

// Rule 2: Use key to track list items
// ❌ Without key
<ul>
<li>A</li>
<li>B</li>
</ul>

// Add C at the beginning
<ul>
<li>C</li> // Recreates everything
<li>A</li>
<li>B</li>
</ul>

// βœ… With key
<ul>
<li key="a">A</li>
<li key="b">B</li>
</ul>

// Add C at the beginning
<ul>
<li key="c">C</li> // Only add C
<li key="a">A</li> // As is
<li key="b">B</li> // As is
</ul>

// Rule 3: Recursively compare children
function diff(oldNode, newNode) {
// 1. Replace if types are different
if (oldNode.type !== newNode.type) {
return { action: 'REPLACE', newNode };
}

// 2. Compare properties
const propsDiff = diffProps(oldNode.props, newNode.props);

// 3. Compare children (recursive)
const childrenDiff = diffChildren(
oldNode.children,
newNode.children
);

return { propsDiff, childrenDiff };
}

5. Reconciliation​

// Reconciliation: Reflecting Virtual DOM to Real DOM

// Phase 1: Render Phase (asynchronous)
// - Compare Virtual DOM
// - Calculate changes
// - Interruptible (React 18 Fiber)

// Phase 2: Commit Phase (synchronous)
// - Update Real DOM
// - Execute lifecycle methods
// - Non-interruptible

// Example: List update
const oldList = [
{ id: 1, name: 'A' },
{ id: 2, name: 'B' },
{ id: 3, name: 'C' }
];

const newList = [
{ id: 1, name: 'A' },
{ id: 3, name: 'C-updated' }, // Modified
{ id: 4, name: 'D' } // Added
// id: 2 deleted
];

// Reconciliation result:
// - id:1 kept
// - id:2 removed
// - id:3 text only updated
// - id:4 added

// Minimize actual DOM operations:
// remove(id:2)
// updateText(id:3, 'C-updated')
// append(id:4)

πŸ’‘ Real Examples​

Basic Virtual DOM Implementation​

// Simple Virtual DOM implementation (for education)

// 1. Create Virtual DOM node
function createElement(type, props = {}, ...children) {
return {
type,
props,
children: children.flat()
};
}

// Use without JSX
const vdom = createElement(
'div',
{ id: 'app', className: 'container' },
createElement('h1', {}, 'Hello Virtual DOM'),
createElement('p', {}, 'This is a paragraph')
);

console.log(vdom);
// {
// type: 'div',
// props: { id: 'app', className: 'container' },
// children: [
// { type: 'h1', props: {}, children: ['Hello Virtual DOM'] },
// { type: 'p', props: {}, children: ['This is a paragraph'] }
// ]
// }

// 2. Convert Virtual DOM to Real DOM
function render(vnode) {
// Text node
if (typeof vnode === 'string' || typeof vnode === 'number') {
return document.createTextNode(vnode);
}

// Element node
const element = document.createElement(vnode.type);

// Apply properties
Object.entries(vnode.props || {}).forEach(([key, value]) => {
if (key === 'className') {
element.setAttribute('class', value);
} else if (key.startsWith('on')) {
// Event listener
const event = key.substring(2).toLowerCase();
element.addEventListener(event, value);
} else {
element.setAttribute(key, value);
}
});

// Render children (recursive)
(vnode.children || []).forEach(child => {
element.appendChild(render(child));
});

return element;
}

// Usage
const domElement = render(vdom);
document.body.appendChild(domElement);

Diffing Algorithm Implementation​

// 3. Diffing Algorithm (simple version)
function diff(oldVNode, newVNode) {
// 1. Remove if new node doesn't exist
if (!newVNode) {
return { type: 'REMOVE' };
}

// 2. Add if old node doesn't exist
if (!oldVNode) {
return { type: 'CREATE', newVNode };
}

// 3. Replace if types are different
if (
typeof oldVNode !== typeof newVNode ||
(typeof oldVNode === 'string' && oldVNode !== newVNode) ||
oldVNode.type !== newVNode.type
) {
return { type: 'REPLACE', newVNode };
}

// 4. Update if same type
if (newVNode.type) {
return {
type: 'UPDATE',
props: diffProps(oldVNode.props, newVNode.props),
children: diffChildren(oldVNode.children, newVNode.children)
};
}

// 5. No change
return { type: 'NONE' };
}

// Compare properties
function diffProps(oldProps = {}, newProps = {}) {
const patches = [];

// Changed or added properties
Object.keys(newProps).forEach(key => {
if (oldProps[key] !== newProps[key]) {
patches.push({ type: 'SET_PROP', key, value: newProps[key] });
}
});

// Removed properties
Object.keys(oldProps).forEach(key => {
if (!(key in newProps)) {
patches.push({ type: 'REMOVE_PROP', key });
}
});

return patches;
}

// Compare children
function diffChildren(oldChildren = [], newChildren = []) {
const patches = [];
const maxLength = Math.max(oldChildren.length, newChildren.length);

for (let i = 0; i < maxLength; i++) {
patches.push(diff(oldChildren[i], newChildren[i]));
}

return patches;
}

Applying Patches​

// 4. Apply patches to Real DOM
function patch(parent, patches, index = 0) {
if (!patches) return;

const element = parent.childNodes[index];

switch (patches.type) {
case 'CREATE':
parent.appendChild(render(patches.newVNode));
break;

case 'REMOVE':
parent.removeChild(element);
break;

case 'REPLACE':
parent.replaceChild(render(patches.newVNode), element);
break;

case 'UPDATE':
// Update properties
patches.props.forEach(propPatch => {
if (propPatch.type === 'SET_PROP') {
element.setAttribute(propPatch.key, propPatch.value);
} else if (propPatch.type === 'REMOVE_PROP') {
element.removeAttribute(propPatch.key);
}
});

// Update children (recursive)
patches.children.forEach((childPatch, i) => {
patch(element, childPatch, i);
});
break;
}
}

Using Virtual DOM in React​

import React, { useState } from 'react';

function Counter() {
const [count, setCount] = useState(0);

// React automatically handles Virtual DOM
return (
<div className="counter">
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
<button onClick={() => setCount(count - 1)}>
Decrement
</button>
</div>
);
}

// Internal operation:
// 1. count changes β†’ setState called
// 2. React creates new Virtual DOM
// 3. Compare with previous Virtual DOM (Diffing)
// 4. Detect only <h1> text changed
// 5. Update only text node of Real DOM
// 6. div and button remain unchanged

// Performance comparison:
// ❌ Vanilla JS: Replace entire div.innerHTML
// βœ… React: Update only <h1> text

Complex List Example​

import React, { useState } from 'react';

function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Exercise', done: false },
{ id: 2, text: 'Study', done: false },
{ id: 3, text: 'Clean', done: false }
]);

const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo
));
};

const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};

return (
<div>
<h1>Todo List</h1>
<ul>
{todos.map(todo => (
<li
key={todo.id} // key is essential for Diffing optimization!
style={{
textDecoration: todo.done ? 'line-through' : 'none',
color: todo.done ? '#888' : '#000'
}}
>
<input
type="checkbox"
checked={todo.done}
onChange={() => toggleTodo(todo.id)}
/>
<span>{todo.text}</span>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
}

// Virtual DOM efficiency:
// Even when checking just one todo
// - ❌ Recreate entire list (X)
// - βœ… Change only that <li>'s style (O)

// Importance of key:
// With key:
// - Efficient when moving/deleting items
// - DOM reusable
// - Component state preserved

// Without key:
// - Entire list recreated
// - Performance degradation
// - Possible component state loss

Performance Optimization Example​

import React, { useState, useMemo, memo } from 'react';

// Component memoization with memo
const TodoItem = memo(function TodoItem({ todo, onToggle, onDelete }) {
console.log(`TodoItem ${todo.id} rendering`);

return (
<li>
<input
type="checkbox"
checked={todo.done}
onChange={() => onToggle(todo.id)}
/>
<span>{todo.text}</span>
<button onClick={() => onDelete(todo.id)}>Delete</button>
</li>
);
});

function OptimizedTodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Exercise', done: false },
{ id: 2, text: 'Study', done: false },
{ id: 3, text: 'Clean', done: false }
]);

// Cache calculation results with useMemo
const activeTodos = useMemo(() => {
console.log('Calculating active todos');
return todos.filter(todo => !todo.done);
}, [todos]);

const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo
));
};

const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};

return (
<div>
<h1>Todo List</h1>
<p>Remaining todos: {activeTodos.length}</p>
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={toggleTodo}
onDelete={deleteTodo}
/>
))}
</ul>
</div>
);
}

// Optimization effects:
// 1. memo: Only changed TodoItem re-renders when one todo changes
// 2. useMemo: Recalculate activeTodos only when todos change
// 3. key: Efficient list updates

// Performance comparison:
// Before optimization: Change 1 of 10 todos β†’ All 10 re-render
// After optimization: Change 1 of 10 todos β†’ Only 1 re-renders

Without vs With Virtual DOM Comparison​

// ❌ Without Virtual DOM (Vanilla JavaScript)
function updateWithoutVirtualDOM() {
const app = document.getElementById('app');
const count = 0;

// Recreate entire HTML every time
setInterval(() => {
count++;
app.innerHTML = `
<div class="container">
<h1>Count: ${count}</h1>
<p>Current time: ${new Date().toLocaleTimeString()}</p>
<button>Click me</button>
</div>
`;
// Problems:
// 1. Recreate entire DOM tree
// 2. Event listeners lost
// 3. Input being typed gets reset
// 4. Scroll position reset
// 5. Animation interrupted
}, 1000);
}

// βœ… With Virtual DOM (React)
function CounterWithVirtualDOM() {
const [count, setCount] = useState(0);
const [time, setTime] = useState(new Date());

useEffect(() => {
const timer = setInterval(() => {
setCount(c => c + 1);
setTime(new Date());
}, 1000);

return () => clearInterval(timer);
}, []);

return (
<div className="container">
<h1>Count: {count}</h1>
<p>Current time: {time.toLocaleTimeString()}</p>
<button onClick={() => alert('Clicked!')}>Click me</button>
</div>
);
}

// Advantages:
// 1. Update only <h1> and <p> text
// 2. Event listeners preserved
// 3. Component state preserved
// 4. Smooth updates
// 5. Minimal DOM manipulation

Actual Performance Measurement​

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

function PerformanceComparison() {
const [items, setItems] = useState([]);
const [renderTime, setRenderTime] = useState(0);

const generateItems = (count) => {
const startTime = performance.now();

const newItems = Array.from({ length: count }, (_, i) => ({
id: i,
value: Math.random()
}));

setItems(newItems);

const endTime = performance.now();
setRenderTime(endTime - startTime);
};

return (
<div>
<h1>Performance Comparison</h1>
<div>
<button onClick={() => generateItems(100)}>
Generate 100
</button>
<button onClick={() => generateItems(1000)}>
Generate 1000
</button>
<button onClick={() => generateItems(10000)}>
Generate 10000
</button>
</div>

<p>Rendering time: {renderTime.toFixed(2)}ms</p>

<div style={{ maxHeight: '400px', overflow: 'auto' }}>
{items.map(item => (
<div key={item.id} style={{ padding: '5px', borderBottom: '1px solid #eee' }}>
Item #{item.id}: {item.value.toFixed(4)}
</div>
))}
</div>
</div>
);
}

// Results (typical case):
// Vanilla JS (entire innerHTML):
// - 100 items: ~50ms
// - 1000 items: ~500ms
// - 10000 items: ~5000ms (laggy)

// React (Virtual DOM):
// - 100 items: ~10ms
// - 1000 items: ~100ms
// - 10000 items: ~1000ms (smooth)

// Virtual DOM performance improvement:
// Initial rendering is similar,
// But 5-10x faster on updates!

Checking with Chrome DevTools​

// How to use React DevTools Profiler

// 1. Install React DevTools
// Install "React Developer Tools" from Chrome Web Store

// 2. Open Profiler tab
// - Click record button
// - Perform actions in app
// - Stop recording

// 3. Analyze results
// - Flamegraph: Component rendering time
// - Ranked: Rendering time ranking
// - Click component: See why it rendered

function ProfiledComponent() {
const [count, setCount] = useState(0);

// Wrap with Profiler to measure rendering performance
return (
<React.Profiler
id="Counter"
onRender={(id, phase, actualDuration) => {
console.log(`${id} (${phase}) took ${actualDuration}ms`);
}}
>
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
</React.Profiler>
);
}

// Console output:
// Counter (mount) took 2.3ms
// Counter (update) took 0.5ms ← Fast thanks to Virtual DOM!

πŸ€” Frequently Asked Questions​

Q1. Is Virtual DOM always faster than Real DOM?​

A: No, it depends on the situation:

// βœ… Virtual DOM is faster:
// 1. Complex UI updates
function ComplexUI() {
const [data, setData] = useState([...]);

// Only some of many components change
return (
<div>
{data.map(item => <ComplexComponent key={item.id} {...item} />)}
</div>
);
// Virtual DOM: Update only changed parts
// Real DOM: Need to recreate everything
}

// 2. Frequent updates
function LiveCounter() {
const [count, setCount] = useState(0);

useEffect(() => {
const timer = setInterval(() => {
setCount(c => c + 1);
}, 100); // 10 updates per second
return () => clearInterval(timer);
}, []);

return <h1>Count: {count}</h1>;
// Virtual DOM: Change only text
// Real DOM: DOM manipulation every time
}

// ❌ Virtual DOM is slower:
// 1. Simple UI (overhead)
function SimpleButton() {
return <button>Click</button>;
}
// Virtual DOM comparison + Real DOM update
// vs
// Direct Real DOM manipulation

// 2. Initial rendering
// Virtual DOM creation + Diffing + Real DOM creation
// vs
// Direct Real DOM creation

// Conclusion:
// Virtual DOM's advantage is "update performance"
// Initial loading can be slightly slower,
// But overall faster for complex apps!

Q2. Why are keys important?​

A: Keys are used by React to identify which items have changed/added/removed:

// ❌ Without key (inefficient)
function BadList() {
const [items, setItems] = useState(['A', 'B', 'C']);

return (
<ul>
{items.map(item => <li>{item}</li>)}
</ul>
);
}

// When adding 'D' at the beginning:
// Before: <li>A</li> <li>B</li> <li>C</li>
// After: <li>D</li> <li>A</li> <li>B</li> <li>C</li>

// Without keys, React:
// - 0th: A β†’ D (changed)
// - 1st: B β†’ A (changed)
// - 2nd: C β†’ B (changed)
// - 3rd: none β†’ C (added)
// Result: All 4 updated! (inefficient)

// βœ… With key (efficient)
function GoodList() {
const [items, setItems] = useState([
{ id: 'a', text: 'A' },
{ id: 'b', text: 'B' },
{ id: 'c', text: 'C' }
]);

return (
<ul>
{items.map(item => (
<li key={item.id}>{item.text}</li>
))}
</ul>
);
}

// When adding 'D' at the beginning:
// Before: key=a, key=b, key=c
// After: key=d, key=a, key=b, key=c

// React identifies by key:
// - key=a: As is (kept)
// - key=b: As is (kept)
// - key=c: As is (kept)
// - key=d: Newly added
// Result: Only 1 added! (efficient)

// Key rules:
// 1. Must be unique among siblings
// 2. Must not change
// 3. Must be predictable

// ❌ Bad key examples:
{items.map((item, index) => <li key={index}>{item}</li>)}
// Index causes problems when order changes!

{items.map(item => <li key={Math.random()}>{item}</li>)}
// New key every time β†’ always recreated!

// βœ… Good key examples:
{items.map(item => <li key={item.id}>{item}</li>)}
// Use data's unique ID

{items.map(item => <li key={item.email}>{item}</li>)}
// Use unique property

Q3. Can you directly manipulate Virtual DOM?​

A: No, React handles it automatically. But optimization is possible:

import React, { useState, useMemo, useCallback, memo } from 'react';

// 1. React.memo - Component memoization
const ExpensiveComponent = memo(function ExpensiveComponent({ value }) {
console.log('ExpensiveComponent rendering');

// Complex calculation
const result = useMemo(() => {
let sum = 0;
for (let i = 0; i < 1000000; i++) {
sum += i;
}
return sum + value;
}, [value]);

return <div>Result: {result}</div>;
});

// Skips re-rendering if props are the same!

// 2. useMemo - Value memoization
function ListComponent({ items }) {
// Recalculate only when items change
const sortedItems = useMemo(() => {
console.log('Sorting...');
return [...items].sort((a, b) => a.value - b.value);
}, [items]);

return (
<ul>
{sortedItems.map(item => (
<li key={item.id}>{item.value}</li>
))}
</ul>
);
}

// 3. useCallback - Function memoization
function Parent() {
const [count, setCount] = useState(0);
const [other, setOther] = useState(0);

// Create new function only when count changes
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []);

return (
<div>
<Child onClick={handleClick} />
<button onClick={() => setOther(other + 1)}>
Other: {other}
</button>
</div>
);
}

const Child = memo(function Child({ onClick }) {
console.log('Child rendering');
return <button onClick={onClick}>Click</button>;
});

// Child doesn't re-render when other changes!

// 4. shouldComponentUpdate / PureComponent (class)
class OptimizedComponent extends React.PureComponent {
render() {
return <div>{this.props.value}</div>;
}
}

// Doesn't re-render if props don't change

// 5. Conditional rendering optimization
function ConditionalRendering({ showDetails, basicInfo, detailInfo }) {
return (
<div>
<BasicInfo data={basicInfo} />
{showDetails && <DetailInfo data={detailInfo} />}
</div>
);
}

// DetailInfo doesn't exist even in Virtual DOM if showDetails is false!

Q4. Are there frameworks without Virtual DOM?​

A: Yes, frameworks like Svelte use different approaches:

// React (uses Virtual DOM)
function Counter() {
const [count, setCount] = useState(0);

return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}

// At runtime:
// 1. Create Virtual DOM
// 2. Diffing
// 3. Update Real DOM

// Svelte (compile-time optimization)
<script>
let count = 0;
</script>

<div>
<h1>Count: {count}</h1>
<button on:click={() => count += 1}>+</button>
</div>

// Compilation result:
function update_count() {
h1.textContent = `Count: ${count}`; // Direct DOM manipulation!
}

// Pros and cons comparison:

// Virtual DOM (React, Vue)
// Pros:
// - Declarative UI
// - Predictable performance
// - Rich ecosystem

// Cons:
// - Memory overhead
// - Diffing cost
// - Large bundle size

// No Virtual DOM (Svelte)
// Pros:
// - Fast performance
// - Small bundle size
// - Low memory usage

// Cons:
// - Smaller ecosystem
// - What about complex apps?
// - Learning curve

// Solid.js (Fine-grained Reactivity)
// React syntax without Virtual DOM
import { createSignal } from 'solid-js';

function Counter() {
const [count, setCount] = createSignal(0);

return (
<div>
<h1>Count: {count()}</h1>
<button onClick={() => setCount(count() + 1)}>+</button>
</div>
);
}

// Update only changed parts precisely!
// Efficient even without Virtual DOM!

Q5. What is React 18's Concurrent Rendering?​

A: A feature that allows interrupting/resuming Virtual DOM updates:

import React, { useState, useTransition, Suspense } from 'react';

// 1. useTransition - Low priority updates
function SearchBox() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();

const handleChange = (e) => {
// Immediate update (high priority)
setQuery(e.target.value);

// Update later (low priority)
startTransition(() => {
// Heavy calculation
const newResults = expensiveSearch(e.target.value);
setResults(newResults);
});
};

return (
<div>
<input
value={query}
onChange={handleChange}
placeholder="Search..."
/>
{isPending && <span>Searching...</span>}
<ul>
{results.map(result => (
<li key={result.id}>{result.title}</li>
))}
</ul>
</div>
);
}

// Behavior:
// 1. User types quickly
// 2. query updates immediately (maintain input responsiveness)
// 3. results update slowly (background)
// 4. Interrupt previous results update if new typing comes in!

// 2. Suspense - Loading state management
const LazyComponent = React.lazy(() => import('./Heavy'));

function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}

// 3. useDeferredValue - Value deferral
function FilteredList({ items, query }) {
const deferredQuery = useDeferredValue(query);

const filteredItems = useMemo(() => {
return items.filter(item =>
item.name.includes(deferredQuery)
);
}, [items, deferredQuery]);

return (
<ul>
{filteredItems.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}

// Concurrent Rendering advantages:
// 1. Maintain responsiveness: UI doesn't lag
// 2. Priority: Important updates first
// 3. Interruptible: Skip unnecessary work
// 4. Smooth transitions: Prevent sudden changes

// Internal operation:
// Time Slicing:
// [Typing] [Rendering] [Typing] [Rendering] ...
// Execute work divided into many small chunks

// Priority:
// Urgent (immediate): Click, typing, hover
// Transition (later): Search results, filtering
// Deferred (latest): Analytics, logging

πŸŽ“ Next Steps​

After understanding Virtual DOM, try learning:

  1. What is React? (document to be written) - Representative library using Virtual DOM
  2. CSR vs SSR vs SSG - Rendering strategy comparison
  3. Web Performance Optimization (document to be written) - Optimization using Virtual DOM

🎬 Conclusion​

Virtual DOM is a core technology of modern web frameworks:

  • Concept: Lightweight copy of Real DOM
  • Principle: Calculate changes with Diffing, then minimal updates
  • Advantage: Improved performance for complex UI updates
  • Key: Enhanced developer experience with declarative programming

Thanks to Virtual DOM, we can focus on UI logic without worrying about performance!