SyntaxStudy
Sign Up
React React Component Tree and Rendering Lifecycle
React Beginner 1 min read

React Component Tree and Rendering Lifecycle

Every React application is a tree of components rooted at a single top-level component — typically ``. React renders this tree top-down: it calls each component function, collects the returned JSX, and uses the result to build or update the real DOM. Child components re-render whenever their parent re-renders, unless they are optimised with `React.memo`. The render cycle has two distinct phases. In the render phase React calls your component functions and calculates what changed; this is pure and may happen multiple times in concurrent mode. In the commit phase React actually updates the DOM, runs layout effects, and then passive effects. Understanding this separation explains why you must not cause side effects directly inside component bodies — use `useEffect` instead. React 18 introduced automatic batching, which groups multiple `setState` calls from async code (like event handlers, fetch callbacks, and setTimeout) into a single re-render. This is a significant performance improvement and means you rarely need to manually optimise batching. Knowing when React renders and when it skips rendering is essential for building efficient applications.
Example
import { useState, useEffect } from 'react';

// Component tree: App → UserCard → Avatar
function Avatar({ src, alt }) {
  return <img src={src} alt={alt} style={{ borderRadius: '50%', width: 64 }} />;
}

function UserCard({ user }) {
  // Rendered whenever App re-renders (or its own state changes)
  return (
    <div className="user-card">
      <Avatar src={user.avatar} alt={user.name} />
      <h3>{user.name}</h3>
      <p>{user.email}</p>
    </div>
  );
}

function App() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  // Side effects go in useEffect, NOT in the render body
  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/users/1')
      .then(r => r.json())
      .then(data => {
        setUser({ name: data.name, email: data.email, avatar: '' });
        setLoading(false);
      });
  }, []); // empty array = run once after first render

  if (loading) return <p>Loading...</p>;
  return <UserCard user={user} />;
}

export default App;