React - Rendering and Virtual DOM
React follows a declarative approach to rendering components, which means you specify what a component should look like, and React takes care of displaying it on the screen. This is the opposite of an imperative approach, where you would write code to manually manipulate the DOM.
The React Rendering Process
The 3 Fundamental Steps
React works like a waiter in a restaurant:
- Trigger - The customer places an order
- Render - The chef prepares the dish
- Commit - The waiter brings the dish
function ExempleRendu() {
const [count, setCount] = useState(0)
console.log('RENDER: Component being rendered')
const handleClick = () => {
console.log('🎯 TRIGGER: State change triggered')
setCount(count + 1) // ← Triggers a new render
}
return (
<div>
<p>Counter: {count}</p>
<button onClick={handleClick}>+1</button>
</div>
)
}
1. Trigger - When React decides to render
React renders in only 2 cases:
function QuandReactRend() {
const [state, setState] = useState(0)
// ✅ CASE 1: Initial render (first time)
useEffect(() => {
console.log('🎬 First render of the component')
}, [])
// ✅ CASE 2: State change
const triggerReRender = () => {
setState(state + 1) // ← Triggers a re-render
}
return (
<div>
<p>State: {state}</p>
<button onClick={triggerReRender}>Change state</button>
</div>
)
}
2. Render - React calls your components
function ExempleRenderPur() {
const [count, setCount] = useState(0)
// ⚠️ This function MUST be pure!
// Same inputs = same outputs
console.log('🔄 Render called with count =', count)
return <div>Count: {count}</div>
}
// ❌ BAD - Side effect during render
function BadExample() {
const [count, setCount] = useState(0)
// ❌ Never do this in the render!
document.title = `Count: ${count}` // Side effect
Math.random() // Non-deterministic
return <div>{count}</div>
}
// ✅ GOOD - Pure render
function GoodExample() {
const [count, setCount] = useState(0)
// ✅ Side effects in useEffect
useEffect(() => {
document.title = `Count: ${count}`
}, [count])
return <div>{count}</div>
}
3. Commit - React updates the DOM
function ExempleCommit() {
const [color, setColor] = useState('red')
return (
<div>
{/* React will only update the color in the DOM */}
<div style={{ backgroundColor: color, padding: '20px' }}>
<h2>Unchanged title</h2>
<p>Unchanged paragraph</p>
<button onClick={() => setColor(color === 'red' ? 'blue' : 'red')}>
Change color
</button>
</div>
</div>
)
}
Virtual DOM - The Magic of React
What is the Virtual DOM?
The Virtual DOM (VDOM) is an in-memory representation of the real DOM. It's like a draft that React uses to optimize updates.
// What you write in JSX
function MonComposant() {
return (
<div className="container">
<h1>Title</h1>
<p>Paragraph</p>
</div>
)
}
// What React creates in Virtual DOM (simplified)
const virtualDOM = {
type: 'div',
props: { className: 'container' },
children: [
{ type: 'h1', props: {}, children: ['Title'] },
{ type: 'p', props: {}, children: ['Paragraph'] }
]
}
The Reconciliation Process
function ExempleReconciliation() {
const [items, setItems] = useState(['A', 'B', 'C'])
const ajouterItem = () => {
setItems([...items, `Item ${Date.now()}`])
}
return (
<div>
<h2>List of items</h2>
{/*
React compares:
- BEFORE: ['A', 'B', 'C']
- AFTER: ['A', 'B', 'C', 'Item 12345']
Result: React ONLY adds the new element to the DOM
*/}
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
<button onClick={ajouterItem}>Add item</button>
</div>
)
}
Why is it important?
// Without Virtual DOM (direct manipulation)
function updateWithoutVDOM() {
// ❌ Slow and inefficient
document.getElementById('title').textContent = 'New title'
document.getElementById('count').textContent = '42'
document.getElementById('status').className = 'active'
// ... 50 other updates
}
// With Virtual DOM (React)
function updateWithVDOM() {
// ✅ React automatically calculates the minimal changes
setTitle('New title')
setCount(42)
setStatus('active')
// React updates ONLY what has changed
}
Practical demonstration
function DemoVirtualDOM() {
const [highlightIndex, setHighlightIndex] = useState(0)
useEffect(() => {
const interval = setInterval(() => {
setHighlightIndex(prev => (prev + 1) % 5)
}, 1000)
return () => clearInterval(interval)
}, [])
return (
<div>
<h3>React updates ONLY the highlighted element</h3>
<ul>
{['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5'].map((item, index) => (
<li
key={index}
style={{
backgroundColor: index === highlightIndex ? 'yellow' : 'transparent',
padding: '10px',
transition: 'background-color 0.3s'
}}
>
{item}
</li>
))}
</ul>
<p>👆 Open the DevTools and look: only the color changes in the DOM!</p>
</div>
)
}