Follow AiTechWorlds on LinkedIn for professional AI content!Follow Now →
18 minLesson 2 of 33
React Core Concepts

Components: Thinking in React

Components: Thinking in React

React's mental model is different from traditional DOM manipulation. Instead of writing instructions that change the page ("add this element", "remove that class"), you describe what the UI should look like for any given state — and React handles the updates. This shift in thinking is the foundation of building React apps well.

What a Component Is

A component is a function that takes some input (props) and returns what to display (JSX). That's it.

function WelcomeBanner({ name, isPremium }: { name: string; isPremium: boolean }) {
    return (
        <div className={`banner ${isPremium ? "banner-gold" : "banner-free"}`}>
            <h2>Welcome back, {name}!</h2>
            {isPremium && <span className="badge">Pro Member</span>}
        </div>
    );
}

The function describes the output. Every time name or isPremium changes, React calls the function again and updates the DOM to match.

The Component Tree

React applications are trees of components. At the root is your App or RootLayout. Everything else is children, grandchildren, and deeper.

App
├── Header
│   ├── Logo
│   ├── Nav
│   │   ├── NavLink (×4)
│   └── UserMenu
│       ├── Avatar
│       └── Dropdown
├── Main
│   └── CoursesPage
│       ├── SearchBar
│       ├── FilterPanel
│       └── CourseGrid
│           └── CourseCard (×12)
└── Footer

Each box is a component. Each component manages its own piece of UI. Changes are localized — when SearchBar state changes, only SearchBar and its children re-render.

How to Break Up a UI

When you see a design, ask three questions:

1. What data changes? Each changing data point is state.

2. Where does the data live? State lives in the lowest common ancestor of all components that need it.

3. What are the natural visual blocks? Each reusable section is a candidate for a component.

// Looking at a product page:

// The whole page — one component
<ProductPage>

    // The gallery changes which image is selected — needs state
    <ImageGallery images={product.images} />  
    
    // Static info — just props
    <ProductInfo 
        title={product.title}
        price={product.price}
        description={product.description}
    />
    
    // Quantity + add to cart changes cart state
    <AddToCartForm productId={product.id} />
    
    // Reviews has its own loading state
    <Reviews productId={product.id} />

</ProductPage>

Component Responsibilities

Each component should have one job. Signs a component needs to be split:

  • It's longer than ~100 lines of JSX
  • It manages multiple unrelated pieces of state
  • Parts of it appear in other places
  • You struggle to name it because it does too many things
// ❌ Doing too much
function UserDashboard() {
    // Fetching users
    // Filtering users
    // Displaying users
    // Handling pagination
    // Displaying a modal
    // Handling form submission
    // ... 300 lines
}

// ✅ Each component has one responsibility
function UserDashboard() {
    const [selectedUser, setSelectedUser] = useState<User | null>(null);
    
    return (
        <div>
            <UserTable onSelectUser={setSelectedUser} />
            {selectedUser && (
                <UserEditModal user={selectedUser} onClose={() => setSelectedUser(null)} />
            )}
        </div>
    );
}

function UserTable({ onSelectUser }: { onSelectUser: (user: User) => void }) {
    // All the user listing logic here
}

function UserEditModal({ user, onClose }: { user: User; onClose: () => void }) {
    // All the editing logic here
}

Props vs State — The Key Distinction

Props: data that comes FROM a parent. Read-only inside the component.

State: data that BELONGS to the component. Can be changed by calling a setter function.

// ❌ Wrong — trying to mutate props
function CounterWrong({ count }: { count: number }) {
    return <button onClick={() => count++}>Clicked {count}</button>;
    //                           ^^^^ This is wrong — can't mutate props
}

// ✅ Correct patterns

// Option 1: If this component owns the count, use state
function CounterOwned() {
    const [count, setCount] = useState(0);
    return <button onClick={() => setCount(c => c + 1)}>Clicked {count}</button>;
}

// Option 2: If the parent owns the count, accept a callback
function CounterControlled({ count, onIncrement }: { count: number; onIncrement: () => void }) {
    return <button onClick={onIncrement}>Clicked {count}</button>;
}

Controlled vs Uncontrolled Components

A controlled component is fully driven by React state. An uncontrolled component manages its own internal state (like a native <input> with no value prop):

// Uncontrolled — the DOM owns the value
function UncontrolledInput() {
    const inputRef = useRef<HTMLInputElement>(null);
    
    function handleSubmit() {
        console.log(inputRef.current?.value);
    }
    
    return <input ref={inputRef} />;
}

// Controlled — React owns the value
function ControlledInput() {
    const [value, setValue] = useState("");
    
    return <input value={value} onChange={e => setValue(e.target.value)} />;
}

Controlled is generally preferred — you can validate, transform, or respond to every keystroke. Use uncontrolled when you just need the final value (a simple search box that submits on Enter).

Compound Components Pattern

For complex components with multiple related parts, the compound components pattern keeps the API clean:

// Instead of prop-heavy APIs:
<Tabs 
    tabs={[...]}
    activeTab={0}
    onTabChange={setTab}
    tabContent={[...]}
/>

// Compound components read naturally:
<Tabs defaultTab="overview">
    <Tabs.List>
        <Tabs.Tab value="overview">Overview</Tabs.Tab>
        <Tabs.Tab value="curriculum">Curriculum</Tabs.Tab>
        <Tabs.Tab value="reviews">Reviews</Tabs.Tab>
    </Tabs.List>
    <Tabs.Panel value="overview">Overview content...</Tabs.Panel>
    <Tabs.Panel value="curriculum">Curriculum content...</Tabs.Panel>
    <Tabs.Panel value="reviews">Reviews content...</Tabs.Panel>
</Tabs>

Each sub-component gets its context from the parent via React Context, not props. The API looks like HTML — easy to read and extend.

When to Extract a Component

Extract when:

  • You copy-paste the same JSX more than once
  • The piece has its own clear state or data concern
  • You want to give it a meaningful name that documents intent

Don't extract just for the sake of it. Three lines of JSX don't need their own component. A 20-line card that appears 15 times definitely does.

Next lesson: Props and component composition — passing data and building flexible component APIs.

📱

Get this course's notes on Telegram!

Free cheat sheets, summaries & practice exercises

Get Notes Free →
!