React Intermediate

Component Composition and Advanced Patterns

CodingerWeb
CodingerWeb
24 views 55 min read

Component Composition in React

Component composition is a powerful pattern in React that allows you to build complex UIs by combining simpler components.

Children Prop Pattern

// Card component that accepts children
function Card({ children, className = '' }) {
    return (
        
{children}
); } // Usage function App() { return (

John Doe

Software Developer

); }

Render Props Pattern

// DataFetcher component using render props
function DataFetcher({ url, render }) {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

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

    return render({ data, loading, error });
}

// Usage
function UserList() {
    return (
         {
                if (loading) return 
Loading...
; if (error) return
Error: {error.message}
; return (
    {data.map(user => (
  • {user.name}
  • ))}
); }} /> ); }

Higher-Order Components (HOCs)

// HOC for adding loading state
function withLoading(WrappedComponent) {
    return function WithLoadingComponent(props) {
        if (props.loading) {
            return 
Loading...
; } return ; }; } // Usage const UserListWithLoading = withLoading(UserList); function App() { const [loading, setLoading] = useState(true); const [users, setUsers] = useState([]); return ( ); }

Compound Components Pattern

// Modal compound component
const Modal = ({ children, isOpen, onClose }) => {
    if (!isOpen) return null;

    return (
        
e.stopPropagation()}> {children}
); }; Modal.Header = ({ children }) => (
{children}
); Modal.Body = ({ children }) => (
{children}
); Modal.Footer = ({ children }) => (
{children}
); // Usage function App() { const [isOpen, setIsOpen] = useState(false); return ( <> setIsOpen(false)}>

Confirm Action

Are you sure you want to delete this item?

); }

Controlled vs Uncontrolled Components

Controlled Component

function ControlledInput() {
    const [value, setValue] = useState('');

    return (
         setValue(e.target.value)}
            placeholder="Controlled input"
        />
    );
}

Uncontrolled Component

function UncontrolledInput() {
    const inputRef = useRef();

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

    return (
        <>
            
            
        
    );
}

Flexible Component API

// Button component with flexible API
function Button({ 
    children, 
    variant = 'primary', 
    size = 'medium',
    disabled = false,
    loading = false,
    leftIcon,
    rightIcon,
    onClick,
    ...props 
}) {
    const baseClasses = 'btn';
    const variantClasses = `btn-${variant}`;
    const sizeClasses = `btn-${size}`;
    const disabledClasses = disabled ? 'btn-disabled' : '';
    const loadingClasses = loading ? 'btn-loading' : '';

    const className = [
        baseClasses,
        variantClasses,
        sizeClasses,
        disabledClasses,
        loadingClasses
    ].filter(Boolean).join(' ');

    return (
        
    );
}

// Usage examples


Practical Exercise

Create a flexible Table component that supports:

  • Compound component pattern (Table.Header, Table.Body, Table.Row, Table.Cell)
  • Sorting functionality
  • Loading and empty states
  • Customizable styling

Best Practices

  • Prefer composition over inheritance
  • Use children prop for flexible content
  • Create reusable, configurable components
  • Keep component APIs simple and intuitive
  • Use TypeScript for better component contracts