Skip to main content

Item Counter Component

The ItemCounter component is a reusable counter that manages item quantities. It demonstrates fundamental React concepts including props, state management with useState, event handlers, and conditional styling.

Complete Implementation

import { useState } from "react"
import cesese from './ItemCounter.module.css'

interface Props {
    name: string;
    quantity?: number;
}

export const ItemCounter = ({ name, quantity }: Props) => {
    const [count, setCount] = useState(Number)

    const handleAdd = () => {
        setCount(count + 1)
    }

    const handleSub = () => {
        if (count === 0) return;
        setCount(count - 1)
    }

    return (
        <section className={cesese['item-row']}>
            <span 
                className="item-text"
                style={{
                    color: count === 0 ? 'red' : 'black',
                }}
            >
                {name}
            </span>
            <button onClick={(event) => {
                handleAdd();
            }}>+1</button>
            <span>{count}</span>
            <button onClick={handleSub}>-1</button>
        </section>
    )
}

Props Interface

The component accepts two props:
interface Props {
    name: string;      // Product name (required)
    quantity?: number; // Initial quantity (optional)
}
The quantity prop is optional (indicated by ?), which means it can be omitted when using the component. However, in the current implementation, it’s not being used to initialize the state.

State Management

useState Hook

The component uses the useState hook to track the current count:
const [count, setCount] = useState(Number)
  • count: The current state value
  • setCount: Function to update the state
  • Number: Passing the Number constructor as initial value (results in NaN initially)
The current implementation uses useState(Number) which initializes count to NaN (Not a Number). To fix this, use useState(0) or useState(quantity ?? 0) to properly initialize with a number value.

Event Handlers

Increment Handler

The handleAdd function increases the count by 1:
const handleAdd = () => {
    setCount(count + 1)
}
Attached to the “+1” button:
<button onClick={handleAdd}>+1</button>

Decrement Handler

The handleSub function decreases the count, with validation:
const handleSub = () => {
    if (count === 0) return;  // Prevent negative values
    setCount(count - 1)
}
This prevents the count from going below zero, which is important for quantity tracking.
The early return pattern (if (count === 0) return;) is a common way to add guard clauses and prevent invalid state updates.

Conditional Styling

The component applies dynamic styling based on the count:
<span 
    className="item-text"
    style={{
        color: count === 0 ? 'red' : 'black',
    }}
>
    {name}
</span>
When the count reaches zero, the product name turns red to indicate it’s out of stock or not selected.

CSS Modules

The component uses CSS Modules for scoped styling:
import cesese from './ItemCounter.module.css'

// Applied as:
<section className={cesese['item-row']}>
ItemCounter.module.css:
.item-row {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 10px;
}

.item-text {
    width: 200px;
}
CSS Modules automatically scope class names to prevent conflicts. The styles['item-row'] syntax accesses the scoped class name.

Component Layout

The component renders a horizontal layout:
[Product Name] [+1] [Count] [-1]
Each element is aligned using flexbox:
  • Product name (fixed width of 200px)
  • Increment button
  • Current count display
  • Decrement button

Key Features

Reusable: Can be used for any product or item
State Management: Tracks quantity independently
Validation: Prevents negative quantities
Visual Feedback: Color changes when count is zero
Type Safe: TypeScript interface ensures correct prop usage

Usage Example

import { ItemCounter } from './shopping-cart/ItemCounter'

function MyComponent() {
    return (
        <div>
            <ItemCounter name="Nintendo Switch" quantity={2} />
            <ItemCounter name="PlayStation 5" quantity={0} />
            <ItemCounter name="Xbox Series X" quantity={5} />
        </div>
    )
}

Potential Improvements

Consider these enhancements:
  • Initialize state with the quantity prop value
  • Add a maximum limit for quantities
  • Emit events when count changes (for parent component tracking)
  • Add increment/decrement by custom amounts
  • Include loading states for async operations
  • Add keyboard support (arrow keys)

Common Use Cases

  • Shopping cart item quantities
  • Inventory management
  • Order forms
  • Ticket selection
  • Guest count selectors
  • Product comparison quantities

Build docs developers (and LLMs) love