12  Component Thinking: React

12.1 The Concept First

In Chapter 10, you learned to fetch content from WordPress via API. But vanilla JavaScript DOM manipulation becomes unwieldy as applications grow. Every change requires finding elements, updating them, keeping track of what’s displayed.

React solves this by introducing a different mental model: component thinking.

Instead of thinking “I need to update this paragraph when data changes,” you think “This component displays data. When data changes, React handles the update.”

Components are self-contained pieces of UI. Each component:

  • Has its own logic
  • Manages its own state (data that changes)
  • Renders its own output
  • Can be reused anywhere

This isn’t just a technical convenience—it’s a fundamentally different way of building interfaces. Once you think in components, you’ll see every UI as a composition of reusable pieces.

12.2 Understanding Through LEGO

Consider how LEGO works:

  • Each brick is self-contained with a consistent interface (the studs)
  • Complex structures emerge from combining simple pieces
  • The same brick can be used in many different creations
  • You can replace one brick without rebuilding everything
  • Instructions show composition, not monolithic assembly

React components work identically:

  • Each component is self-contained with consistent interfaces (props)
  • Complex UIs emerge from combining simple components
  • The same component can be reused throughout your application
  • You can update one component without touching others
  • Your code shows composition: components containing components

A webpage isn’t a monolithic HTML document—it’s a tree of components, each responsible for its own piece of the interface.

TipThe Decomposition Instinct

When you see any interface, practice breaking it into components. A navigation bar? That’s a Nav component containing NavLink components. A product card? That’s a ProductCard containing Image, Title, Price, and Button components. This instinct transfers to any component framework.

12.3 Discovering Components with Your AI Partner

Exploration 1: UI Decomposition

Let’s practice component thinking before writing code:

Ask your AI:
Look at a typical e-commerce product listing page (grid of products,
filters on the side, pagination at bottom). How would you break this
into React components? Draw a diagram showing which components
contain which other components.

Your diagram might look like:

ProductPage
├── FilterSidebar
│   ├── CategoryFilter
│   ├── PriceFilter
│   └── RatingFilter
├── ProductGrid
│   └── ProductCard (repeated)
│       ├── ProductImage
│       ├── ProductTitle
│       ├── ProductPrice
│       └── AddToCartButton
└── Pagination
Continue the conversation:
For each component you identified, what data would it need? Where
would that data come from?

Exploration 2: State vs Props

React has two ways data lives in components: state (data the component owns and can change) and props (data passed from a parent).

Ask your AI:
Explain the difference between state and props in React using a
family analogy. Props are like what? State is like what? How do
they interact?

A common analogy:

  • Props are like instructions parents give to children: “Here’s your allowance amount, here’s your bedtime.” The child receives them but doesn’t change them.
  • State is like the child’s own possessions: “I have $5 saved, I’m currently awake.” The child controls these and they can change.
Continue the conversation:
For a shopping cart component, what would be props and what would
be state? Walk me through why each belongs in its category.

Exploration 3: Data Flow

Data in React flows one direction: parent to child (downward).

Ask your AI:
React data flows "one way"—from parent to child components. Why is
this design choice valuable? What problems does it prevent compared
to data flowing any direction?

One-way data flow means:

  • You always know where data came from
  • Debugging is simpler (trace upward to find the source)
  • Components are predictable
  • Changes don’t cause cascading side effects
Continue the conversation:
If data flows only downward, how does a child component communicate
back to a parent? Give me a simple example.

12.4 From Concept to Code

Let’s build your React understanding progressively.

Setting Up React

The simplest way to start is with Vite, a modern build tool:

npm create vite@latest my-react-app -- --template react
cd my-react-app
npm install
npm run dev

This creates a React project and starts a development server. Open http://localhost:5173 to see it running.

NotePrerequisites

You need Node.js installed. Download from nodejs.org if you haven’t already.

JSX: HTML in JavaScript

React uses JSX—a syntax that lets you write HTML-like code in JavaScript:

function Welcome() {
    return (
        <div>
            <h1>Hello, World!</h1>
            <p>Welcome to React.</p>
        </div>
    );
}

This looks like HTML but it’s actually JavaScript. JSX gets transformed into function calls that create elements.

Key JSX differences from HTML:

HTML JSX
class="..." className="..."
for="..." htmlFor="..."
onclick="..." onClick={...}
Self-closing optional Self-closing required: <img />

JavaScript expressions go inside curly braces:

function Greeting({ name }) {
    const currentHour = new Date().getHours();
    const greeting = currentHour < 12 ? 'Good morning' : 'Good afternoon';

    return (
        <h1>{greeting}, {name}!</h1>
    );
}

Your First Component

Components are functions that return JSX:

function ProductCard() {
    return (
        <div className="product-card">
            <h2>Coffee Mug</h2>
            <p>$12.99</p>
            <button>Add to Cart</button>
        </div>
    );
}

Use components like HTML elements:

function App() {
    return (
        <div>
            <h1>Our Products</h1>
            <ProductCard />
            <ProductCard />
            <ProductCard />
        </div>
    );
}

Three product cards appear—but they’re all identical. To make them different, we need props.

Props: Configuring Components

Props pass data from parent to child:

function ProductCard({ title, price }) {
    return (
        <div className="product-card">
            <h2>{title}</h2>
            <p>${price}</p>
            <button>Add to Cart</button>
        </div>
    );
}

function App() {
    return (
        <div>
            <h1>Our Products</h1>
            <ProductCard title="Coffee Mug" price={12.99} />
            <ProductCard title="Tea Cup" price={9.99} />
            <ProductCard title="Water Bottle" price={14.99} />
        </div>
    );
}

Now each card displays different data. The component is reusable—same structure, different content.

Props can be any JavaScript value:

<ProductCard
    title="Coffee Mug"
    price={12.99}
    inStock={true}
    tags={['kitchen', 'drinkware']}
    onAddToCart={() => console.log('Added!')}
/>

State: Data That Changes

Props come from outside; state lives inside the component and can change.

import { useState } from 'react';

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

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

useState returns two things:

  1. The current value (count)
  2. A function to update it (setCount)

When you call setCount, React re-renders the component with the new value. You don’t manually update the DOM—React handles it.

Ask your AI:
Walk me through what happens when I click the button in this Counter
component. What does React do behind the scenes?

More State Examples

Toggle:

function Toggle() {
    const [isOn, setIsOn] = useState(false);

    return (
        <button onClick={() => setIsOn(!isOn)}>
            {isOn ? 'ON' : 'OFF'}
        </button>
    );
}

Form input:

function SearchBox() {
    const [query, setQuery] = useState('');

    return (
        <div>
            <input
                type="text"
                value={query}
                onChange={(e) => setQuery(e.target.value)}
                placeholder="Search..."
            />
            <p>Searching for: {query}</p>
        </div>
    );
}

Lists and Keys

To render a list, use JavaScript’s map:

function ProductList({ products }) {
    return (
        <div className="product-list">
            {products.map(product => (
                <ProductCard
                    key={product.id}
                    title={product.title}
                    price={product.price}
                />
            ))}
        </div>
    );
}

The key prop helps React track which items changed. Use a unique identifier (like id), not the array index.

useEffect: Side Effects and Data Fetching

Components sometimes need to do things besides rendering: fetch data, set up subscriptions, update the document title. These are side effects.

import { useState, useEffect } from 'react';

function PostList() {
    const [posts, setPosts] = useState([]);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        async function fetchPosts() {
            try {
                const response = await fetch(
                    'https://jsonplaceholder.typicode.com/posts?_limit=5'
                );
                if (!response.ok) throw new Error('Failed to fetch');
                const data = await response.json();
                setPosts(data);
            } catch (err) {
                setError(err.message);
            } finally {
                setLoading(false);
            }
        }

        fetchPosts();
    }, []); // Empty array means "run once on mount"

    if (loading) return <p>Loading...</p>;
    if (error) return <p>Error: {error}</p>;

    return (
        <ul>
            {posts.map(post => (
                <li key={post.id}>{post.title}</li>
            ))}
        </ul>
    );
}

The useEffect hook:

  • Runs after the component renders
  • The empty [] dependency array means “run only once”
  • Perfect for data fetching when component loads

Connecting to WordPress

Remember the WordPress REST API from Chapter 10? Let’s fetch WordPress posts:

import { useState, useEffect } from 'react';

function WordPressPosts() {
    const [posts, setPosts] = useState([]);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        fetch('http://your-site.local/wp-json/wp/v2/posts?_embed')
            .then(res => res.json())
            .then(data => {
                setPosts(data);
                setLoading(false);
            })
            .catch(err => {
                console.error(err);
                setLoading(false);
            });
    }, []);

    if (loading) return <p>Loading posts...</p>;

    return (
        <div>
            {posts.map(post => (
                <article key={post.id}>
                    <h2 dangerouslySetInnerHTML={{ __html: post.title.rendered }} />
                    <div dangerouslySetInnerHTML={{ __html: post.excerpt.rendered }} />
                </article>
            ))}
        </div>
    );
}
WarningdangerouslySetInnerHTML

WordPress returns HTML in rendered fields. React requires dangerouslySetInnerHTML to insert raw HTML. Only use this with trusted content (like your own WordPress site)—never with user input.

Component Composition

Build complex UIs by composing simple components:

function App() {
    return (
        <div className="app">
            <Header />
            <main>
                <Sidebar />
                <PostList />
            </main>
            <Footer />
        </div>
    );
}

function Header() {
    return (
        <header>
            <Logo />
            <Navigation />
        </header>
    );
}

function Navigation() {
    return (
        <nav>
            <NavLink to="/">Home</NavLink>
            <NavLink to="/about">About</NavLink>
            <NavLink to="/contact">Contact</NavLink>
        </nav>
    );
}

Each component is small, focused, and understandable. The composition creates the full application.

12.5 Building Your Mental Model

The Component Tree

Your application forms a tree of components:

App
├── Header
│   ├── Logo
│   └── Navigation
│       └── NavLink (×3)
├── Main
│   ├── Sidebar
│   └── PostList
│       └── PostCard (×n)
└── Footer

Data flows down this tree. State lives in the component that “owns” it—often a parent that passes it to children.

When State Should “Lift Up”

If two sibling components need the same data, the state should live in their common parent:

Before (broken):                After (working):
ProductFilter (has state)       ProductPage (has state)
ProductList (needs state)       ├── ProductFilter (receives props)
(How does List get Filter's     └── ProductList (receives props)
 state? They're siblings!)

This is called “lifting state up”—moving state to the lowest common ancestor.

The Rendering Cycle

State Changes
     ↓
React Re-renders Component
     ↓
New JSX Generated
     ↓
React Compares to Previous
     ↓
Only Changed Parts Update in DOM

You don’t touch the DOM. You declare what should appear based on state. React efficiently updates only what changed.

12.6 Business Applications

Reusability

Build a Button component once with variants (primary, secondary, danger). Use it everywhere. Update the design in one place, and every button updates.

Maintainability

When a feature needs updating, you know exactly which component to modify. Components are isolated—changes don’t ripple unexpectedly.

Team Collaboration

Different developers can work on different components simultaneously. The interface (props) defines how components connect—teams don’t need to understand each other’s implementation details.

Testing

Components can be tested in isolation. Does ProductCard render correctly with these props? Does Counter increment when clicked? Unit tests are straightforward.

Design Systems

React components naturally form design systems. Your Button, Card, Input, and Modal components become a library that enforces consistency across the application.

NoteULO Connection

This develops ULO 1 (effective web applications) and ULO 4 (technology selection). React’s component model is influential across the industry—understanding it prepares you for Vue, Angular, and other frameworks that share similar concepts.

12.7 Practice Exercises

NoteExercise Levels
  • Level 1: Direct application
  • Level 2: Minor modifications
  • Level 3: Combining concepts
  • Level 4: Problem-solving
  • Level 5: Open-ended design

Exercise 9.1: First Component (Level 1)

Create a React project with Vite and build a Greeting component that:

  1. Accepts a name prop
  2. Displays “Hello, {name}!”
  3. Is used three times in App with different names

Exercise 9.2: Stateful Component (Level 2)

Build a LikeButton component that:

  1. Displays a heart icon (can be emoji ❤️)
  2. Shows a like count starting at 0
  3. Increments the count when clicked
  4. Changes colour when count > 0

Exercise 9.3: List Rendering (Level 3)

Build a TaskList component that:

  1. Accepts an array of tasks as props
  2. Renders each task as a TaskItem component
  3. Each TaskItem shows the task text and a “Complete” button
  4. Clicking “Complete” removes the task (hint: lift state up)

Exercise 9.4: Data Fetching (Level 4)

Build a UserList component that:

  1. Fetches users from https://jsonplaceholder.typicode.com/users
  2. Displays loading state while fetching
  3. Handles errors gracefully
  4. Renders each user as a UserCard with name and email
  5. Allows clicking a user to show more details

Exercise 9.5: WordPress Integration (Level 5)

Build a mini blog frontend that:

  1. Fetches posts from your local WordPress site (or JSONPlaceholder)
  2. Displays posts in a PostList component
  3. Allows clicking a post to see full content in a PostDetail component
  4. Includes a search box that filters posts by title
  5. Handles loading and error states throughout

Document your component structure and explain your state management decisions.

12.8 Chapter Summary

  • Components are reusable, self-contained UI pieces
  • Props pass data from parent to child (read-only)
  • State is data the component owns and can change
  • useState creates state; useEffect handles side effects
  • Data flows one direction: parent to child
  • Component thinking is transferable to other frameworks

12.9 Reflection

Before moving to Chapter 10, ensure you can:

12.10 Your Learning Journal

Record your responses to these prompts:

  1. Decomposition Practice: Pick any website you use. Sketch how you’d break it into React components. What would be reusable?

  2. State Analysis: Think about a form you’ve filled out online. What state would it need? Where would that state live?

  3. AI Conversation Reflection: What React concept was hardest to grasp? What question to your AI partner helped clarify it?

  4. Mental Model Shift: How is thinking in components different from the vanilla JavaScript approach in earlier chapters? What’s easier? What’s harder?

12.11 Next Steps

You can now build component-based interfaces and fetch data from APIs. But styling components from scratch takes time.

In Chapter 13, we’ll explore CSS frameworks—Bootstrap and Tailwind—that provide pre-built styles and utilities. Combined with React, these tools enable rapid professional development without designing every button and layout from scratch.