Understanding how React renders components and updates the DOM
Rendering is the process of React calling your components to figure out what should be displayed on the screen. Understanding how React renders helps you write more performant applications and debug rendering issues.
Updating a component’s state automatically queues a render. You can think of this like a restaurant guest ordering tea, dessert, and all sorts of things after putting in their first order, depending on the state of their thirst or hunger.
When you write JSX, it creates React elements - lightweight descriptions of what to render:
Copy
Ask AI
// This JSXconst element = <h1 className="greeting">Hello, world!</h1>;// Creates this React element (simplified)const element = { type: 'h1', props: { className: 'greeting', children: 'Hello, world!' }};
React elements are immutable. According to React’s source code in ReactJSXElement.js, once you create an element, you can’t change its children or attributes. An element is like a single frame in a movie: it represents the UI at a certain point in time.
The collection of all React elements forms a tree structure, often called the “virtual DOM” (though React doesn’t officially use this term):
After rendering your components, React needs to figure out what changed. This process is called reconciliation.React compares the new element tree with the previous one:
When rendering lists, React uses keys to match children:
Copy
Ask AI
// Without keys (inefficient)<ul> <li>First</li> <li>Second</li></ul>// Add item at the beginning<ul> <li>Zero</li> {/* React thinks this is "First" changed */} <li>First</li> {/* React thinks this is "Second" changed */} <li>Second</li> {/* React thinks this is new */}</ul>
Copy
Ask AI
// With keys (efficient)<ul> <li key="first">First</li> <li key="second">Second</li></ul>// Add item at the beginning<ul> <li key="zero">Zero</li> {/* React knows this is new */} <li key="first">First</li> {/* React knows this stayed the same */} <li key="second">Second</li> {/* React knows this stayed the same */}</ul>
Never use array indexes as keys if the list can be reordered, filtered, or have items added/removed. This can cause serious bugs and performance issues.
Copy
Ask AI
// ❌ Bad: using index as keyitems.map((item, index) => <li key={index}>{item}</li>)// ✅ Good: using stable IDitems.map(item => <li key={item.id}>{item.text}</li>)
import { memo } from 'react';// Without memo: re-renders whenever parent re-rendersfunction ExpensiveChild({ data }) { return <div>{expensiveCalculation(data)}</div>;}// With memo: only re-renders if props changeconst ExpensiveChild = memo(function ExpensiveChild({ data }) { return <div>{expensiveCalculation(data)}</div>;});
function ProfilePage({ userId }) { // This Profile component will unmount and remount // with fresh state when userId changes return <Profile key={userId} userId={userId} />;}
// ❌ Missing dependency causes stale closureuseEffect(() => { setInterval(() => { console.log(count); // Always logs 0 }, 1000);}, []); // Missing 'count'// ✅ Include all dependenciesuseEffect(() => { const id = setInterval(() => { console.log(count); // Logs current count }, 1000); return () => clearInterval(id);}, [count]);
If you’re experiencing performance issues, use React DevTools Profiler to identify slow components before optimizing. Premature optimization can make code more complex without meaningful performance gains.