Skip to content

React Props

Overview

Props (short for properties) is the mechanism for passing data between React components. Props make components reusable and flexible, and are the foundation for building dynamic React applications. This chapter will deeply learn how to use Props, validation, and best practices.

🔄 Props Basics

What is Props

jsx
// Props is data passed from parent component to child component
function Greeting({ name, age }) {
    return (
        <div>
            <h2>Hello, {name}!</h2>
            <p>You are {age} years old</p>
        </div>
    );
}

function App() {
    return (
        <div>
            <Greeting name="John" age={25} />
            <Greeting name="Jane" age={30} />
        </div>
    );
}

Props Characteristics

  • Read-only: Components cannot modify their own props
  • Unidirectional data flow: Data flows from parent to child
  • Dynamic: Props can be variables, expressions, or functions

📦 Props Types

Basic Data Types

jsx
function UserCard({
    name,           // string
    age,            // number
    isActive,       // boolean
    hobbies,        // array
    profile,        // object
    onEdit          // function
}) {
    return (
        <div className={`user-card ${isActive ? 'active' : 'inactive'}`}>
            <h3>{name}</h3>
            <p>Age: {age}</p>
            <p>Status: {isActive ? 'Online' : 'Offline'}</p>

            <div className="hobbies">
                <h4>Hobbies:</h4>
                <ul>
                    {hobbies.map((hobby, index) => (
                        <li key={index}>{hobby}</li>
                    ))}
                </ul>
            </div>

            <p>Email: {profile.email}</p>

            <button onClick={() => onEdit(profile.id)}>
                Edit User
            </button>
        </div>
    );
}

// Using the component
function App() {
    const user = {
        id: 1,
        email: 'john@example.com',
        address: 'New York'
    };

    const handleEdit = (userId) => {
        console.log(`Edit user ID: ${userId}`);
    };

    return (
        <UserCard
            name="John"
            age={28}
            isActive={true}
            hobbies={['Reading', 'Swimming', 'Coding']}
            profile={user}
            onEdit={handleEdit}
        />
    );
}

Special Props

children Property

jsx
// children is a special prop representing the component's children
function Card({ title, children }) {
    return (
        <div className="card">
            <h3 className="card-title">{title}</h3>
            <div className="card-content">
                {children}
            </div>
        </div>
    );
}

function App() {
    return (
        <div>
            <Card title="User Info">
                <p>Name: John</p>
                <p>Email: john@example.com</p>
                <button>Edit</button>
            </Card>

            <Card title="Product List">
                <ul>
                    <li>Product A</li>
                    <li>Product B</li>
                    <li>Product C</li>
                </ul>
            </Card>
        </div>
    );
}

Render Props Pattern

jsx
function DataProvider({ render, url }) {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        fetch(url)
            .then(response => response.json())
            .then(data => {
                setData(data);
                setLoading(false);
            });
    }, [url]);

    return render({ data, loading });
}

// Using render props
function App() {
    return (
        <DataProvider
            url="/api/users"
            render={({ data, loading }) => (
                <div>
                    {loading ? (
                        <p>Loading...</p>
                    ) : (
                        <ul>
                            {data?.map(user => (
                                <li key={user.id}>{user.name}</li>
                            ))}
                        </ul>
                    )}
                </div>
            )}
        />
    );
}

⚙️ Props Default Values

Using Default Parameters

jsx
function Button({
    children,
    type = 'button',
    variant = 'primary',
    size = 'medium',
    disabled = false,
    onClick = () => {}
}) {
    const className = `btn btn-${variant} btn-${size}`;

    return (
        <button
            type={type}
            className={className}
            disabled={disabled}
            onClick={onClick}
        >
            {children}
        </button>
    );
}

// Using default values
function App() {
    return (
        <div>
            <Button>Default Button</Button>
            <Button variant="secondary" size="large">
                Secondary Button
            </Button>
            <Button variant="danger" disabled>
                Disabled Button
            </Button>
        </div>
    );
}

Using defaultProps (Class Components)

jsx
class Welcome extends Component {
    static defaultProps = {
        name: 'Guest',
        showTime: false
    };

    render() {
        const { name, showTime } = this.props;
        return (
            <div>
                <h1>Welcome, {name}!</h1>
                {showTime && (
                    <p>Current time: {new Date().toLocaleString()}</p>
                )}
            </div>
        );
    }
}

🔍 Props Destructuring and Passing

Props Destructuring

jsx
// Basic destructuring
function UserInfo({ user: { name, email, age } }) {
    return (
        <div>
            <h2>{name}</h2>
            <p>Email: {email}</p>
            <p>Age: {age}</p>
        </div>
    );
}

// Renaming destructuring
function ProductCard({
    product: {
        name: productName,
        price: productPrice
    },
    currency = '$'
}) {
    return (
        <div>
            <h3>{productName}</h3>
            <p>Price: {currency}{productPrice}</p>
        </div>
    );
}

// Rest parameters
function Input({ label, error, ...inputProps }) {
    return (
        <div className="form-group">
            <label>{label}</label>
            <input {...inputProps} />
            {error && <span className="error">{error}</span>}
        </div>
    );
}

// Usage example
function LoginForm() {
    return (
        <form>
            <Input
                label="Username"
                type="text"
                placeholder="Enter username"
                required
            />
            <Input
                label="Password"
                type="password"
                placeholder="Enter password"
                error="Password must be at least 6 characters"
                required
            />
        </form>
    );
}

Props Passing Patterns

jsx
// Pass through props
function FormInput(props) {
    return <input className="form-input" {...props} />;
}

// Selective passing
function CustomButton({ variant, size, children, ...buttonProps }) {
    const className = `btn btn-${variant} btn-${size}`;

    return (
        <button className={className} {...buttonProps}>
            {children}
        </button>
    );
}

// Composition passing
function IconButton({ icon, children, ...props }) {
    return (
        <CustomButton {...props}>
            <span className="icon">{icon}</span>
            {children}
        </CustomButton>
    );
}

🛡️ Props Validation

PropTypes (Development)

jsx
import PropTypes from 'prop-types';

function UserProfile({
    user,
    showEmail,
    onEdit,
    theme
}) {
    return (
        <div className={`user-profile theme-${theme}`}>
            <h2>{user.name}</h2>
            <p>Age: {user.age}</p>
            {showEmail && <p>Email: {user.email}</p>}
            <button onClick={() => onEdit(user.id)}>Edit</button>
        </div>
    );
}

UserProfile.propTypes = {
    user: PropTypes.shape({
        id: PropTypes.number.isRequired,
        name: PropTypes.string.isRequired,
        age: PropTypes.number.isRequired,
        email: PropTypes.string
    }).isRequired,
    showEmail: PropTypes.bool,
    onEdit: PropTypes.func.isRequired,
    theme: PropTypes.oneOf(['light', 'dark'])
};

UserProfile.defaultProps = {
    showEmail: false,
    theme: 'light'
};

TypeScript Type Definitions

tsx
interface User {
    id: number;
    name: string;
    age: number;
    email?: string;
}

interface UserProfileProps {
    user: User;
    showEmail?: boolean;
    onEdit: (userId: number) => void;
    theme?: 'light' | 'dark';
}

function UserProfile({
    user,
    showEmail = false,
    onEdit,
    theme = 'light'
}: UserProfileProps) {
    return (
        <div className={`user-profile theme-${theme}`}>
            <h2>{user.name}</h2>
            <p>Age: {user.age}</p>
            {showEmail && user.email && <p>Email: {user.email}</p>}
            <button onClick={() => onEdit(user.id)}>Edit</button>
        </div>
    );
}

🎯 Props Best Practices

1. Keep Props Interface Simple

jsx
// ❌ Too many props
function ComplexComponent({
    title, subtitle, content, imageUrl, imageAlt,
    showImage, showSubtitle, titleColor, contentColor,
    onClick, onHover, onFocus, className, style
}) {
    // Complex component implementation
}

// ✅ Use objects to organize related props
function BetterComponent({
    content: { title, subtitle, body },
    image: { url, alt, show = true },
    styling: { titleColor, contentColor, className, style },
    handlers: { onClick, onHover, onFocus }
}) {
    // Clearer component implementation
}

2. Use Callback Functions Reasonably

jsx
function TodoItem({ todo, onUpdate }) {
    const handleToggle = () => {
        onUpdate(todo.id, { completed: !todo.completed });
    };

    const handleEdit = (newText) => {
        onUpdate(todo.id, { text: newText });
    };

    return (
        <div className="todo-item">
            <input
                type="checkbox"
                checked={todo.completed}
                onChange={handleToggle}
            />
            <EditableText
                text={todo.text}
                onSave={handleEdit}
            />
        </div>
    );
}

3. Avoid Props Mutation

jsx
// ❌ Wrong: Modifying props
function BadComponent({ items }) {
    items.push({ id: Date.now(), text: 'new item' }); // Don't do this
    return <ul>{items.map(item => <li key={item.id}>{item.text}</li>)}</ul>;
}

// ✅ Correct: Create new array
function GoodComponent({ items, onAddItem }) {
    const handleAdd = () => {
        const newItem = { id: Date.now(), text: 'new item' };
        onAddItem(newItem);
    };

    return (
        <div>
            <ul>
                {items.map(item => (
                    <li key={item.id}>{item.text}</li>
                ))}
            </ul>
            <button onClick={handleAdd}>Add Item</button>
        </div>
    );
}

📊 Performance Considerations

Using memo for Optimization

jsx
import { memo } from 'react';

const ExpensiveItem = memo(function Item({ item, onSelect }) {
    console.log(`Rendering item: ${item.id}`);

    return (
        <div onClick={() => onSelect(item.id)}>
            <h3>{item.title}</h3>
            <p>{item.description}</p>
        </div>
    );
});

// Custom comparison function
const OptimizedItem = memo(function Item({ item, selected }) {
    return (
        <div className={selected ? 'selected' : ''}>
            <h3>{item.title}</h3>
        </div>
    );
}, (prevProps, nextProps) => {
    return prevProps.item.id === nextProps.item.id &&
           prevProps.selected === nextProps.selected;
});

📝 Chapter Summary

Props is the core mechanism for React component communication. Mastering Props usage is essential for building maintainable React applications.

Key Takeaways

  • ✅ Props is read-only, used for parent-child component communication
  • ✅ Supports various data types and function passing
  • ✅ children is a special prop
  • ✅ Can set default values and perform type validation
  • ✅ Reasonable props design improves component usability

Best Practices

  1. Keep Props interface simple and clear
  2. Use TypeScript or PropTypes for type checking
  3. Avoid excessive props passing
  4. Use default values reasonably
  5. Consider performance impact, use memo appropriately

Continue Learning: Next Chapter - React State

Content is for learning and research only.