Components and Props

Components are the building blocks of React. Props are how data flows between them. Understanding this relationship is the foundation of React development.

Component Composition

JSX
// Layout component
function PageLayout({ children, sidebar }) {
    return (
        <div className="page">
            <header className="header">
                <h1>My App</h1>
                <nav>Home | About | Contact</nav>
            </header>
            <div className="content">
                {sidebar && <aside className="sidebar">{sidebar}</aside>}
                <main>{children}</main>
            </div>
        </div>
    );
}

// Using composition
function Dashboard() {
    return (
        <PageLayout sidebar={<UserInfo />}>
            <h2>Dashboard</h2>
            <Stats />
            <RecentActivity />
        </PageLayout>
    );
}

Props Deep Dive

JSX
// Destructuring props
function ProductCard({ name, price, image, inStock, onAddToCart }) {
    return (
        <div className="product-card">
            <img src={image} alt={name} />
            <h3>{name}</h3>
            <p>${price.toFixed(2)}</p>
            {inStock ? (
                <button onClick={onAddToCart}>Add to Cart</button>
            ) : (
                <span className="out-of-stock">Out of Stock</span>
            )}
        </div>
    );
}

// Using it
function ProductList({ products }) {
    const handleAddToCart = (product) => {
        console.log(`Adding ${product.name} to cart`);
    };

    return (
        <div className="product-grid">
            {products.map(product => (
                <ProductCard
                    key={product.id}
                    {...product}              // Spread all properties as props
                    onAddToCart={() => handleAddToCart(product)}
                />
            ))}
        </div>
    );
}

Children Prop

JSX
// Any content between tags becomes the children prop
function Card({ title, children }) {
    return (
        <div className="card">
            <h2>{title}</h2>
            <div className="card-content">
                {children}
            </div>
        </div>
    );
}

// Usage
<Card title="User Profile">
    <p>Name: Alice</p>
    <p>Email: alice@example.com</p>
    <button>Edit Profile</button>
</Card>

Component Patterns

Container/Presentational

JSX
// Presentational (dumb) — just renders UI
function TodoItem({ todo, onToggle }) {
    return (
        <li
            className={todo.completed ? 'completed' : ''}
            onClick={() => onToggle(todo.id)}
        >
            {todo.text}
        </li>
    );
}

// Container (smart) — manages state and logic
function TodoListContainer() {
    const [todos, setTodos] = useState([
        { id: 1, text: 'Learn React', completed: false },
        { id: 2, text: 'Build an app', completed: false },
    ]);

    const toggleTodo = (id) => {
        setTodos(todos.map(todo =>
            todo.id === id ? { ...todo, completed: !todo.completed } : todo
        ));
    };

    return (
        <ul>
            {todos.map(todo => (
                <TodoItem key={todo.id} todo={todo} onToggle={toggleTodo} />
            ))}
        </ul>
    );
}

Controlled vs Uncontrolled Components

JSX
// Controlled — React controls the value
function ControlledInput() {
    const [value, setValue] = useState('');

    return (
        <input
            type="text"
            value={value}
            onChange={(e) => setValue(e.target.value)}
        />
    );
}

// Uncontrolled — DOM manages the value
function UncontrolledInput() {
    const inputRef = useRef();

    const handleSubmit = () => {
        console.log(inputRef.current.value);
    };

    return <input type="text" ref={inputRef} />;
}

PropTypes and Default Props

JSX
import PropTypes from 'prop-types';

function UserAvatar({ src, name, size = 40 }) {
    return (
        <img
            src={src}
            alt={name}
            style={{ width: size, height: size, borderRadius: '50%' }}
        />
    );
}

UserAvatar.propTypes = {
    src: PropTypes.string.isRequired,
    name: PropTypes.string.isRequired,
    size: PropTypes.number,
};

UserAvatar.defaultProps = {
    size: 40,
};

💡 Tip: Keep components small and focused. If a component has more than 100 lines, consider splitting it. Props flow down, events flow up.

Next: State and Events