Forgetting to destructure props correctly

Loading

Proper Prop Destructuring in React Components

Destructuring props incorrectly is a common source of bugs and maintenance headaches in React applications. Here’s how to handle prop destructuring properly to write cleaner, more reliable components.

Common Destructuring Mistakes

// ❌ Problematic patterns:

// 1. Not destructuring at all
function UserCard(props) {
  return (
    <div>
      <h2>{props.user.name}</h2>
      <p>{props.user.email}</p>
    </div>
  );
}

// 2. Partial destructuring
function Product({ product, onAddToCart }) {
  return (
    <div>
      <h3>{product.name}</h3>
      <p>{product.description}</p>
      <button onClick={() => onAddToCart(product.id)}>
        Add to Cart
      </button>
    </div>
  );
}

// 3. Destructuring inside render
function Comment({ comment }) {
  const { author, text } = comment;
  return (
    <div>
      <strong>{author}</strong>: {text}
    </div>
  );
}

Correct Destructuring Patterns

1. Full Prop Destructuring

// ✅ Proper destructuring at parameter level
function UserCard({ user, onSelect, className = 'card' }) {
  return (
    <div className={className} onClick={() => onSelect(user.id)}>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}

2. Nested Destructuring

// ✅ Clean nested destructuring
function Product({
  product: {
    id,
    name,
    description,
    price
  },
  onAddToCart
}) {
  return (
    <div className="product">
      <h3>{name}</h3>
      <p>{description}</p>
      <span>${price.toFixed(2)}</span>
      <button onClick={() => onAddToCart(id)}>
        Add to Cart
      </button>
    </div>
  );
}

3. Default Values with Destructuring

// ✅ Default values in destructuring
function Avatar({
  src = '/default-avatar.png',
  alt = 'User avatar',
  size = 'medium'
}) {
  const sizeClasses = {
    small: 'w-8 h-8',
    medium: 'w-12 h-12',
    large: 'w-16 h-16'
  };

  return (
    <img 
      src={src} 
      alt={alt}
      className={`rounded-full ${sizeClasses[size]}`}
    />
  );
}

Advanced Destructuring Techniques

1. Rest Parameters for Props Forwarding

// ✅ Using rest parameters
function Button({ variant = 'primary', children, ...props }) {
  const variantClasses = {
    primary: 'bg-blue-500 text-white',
    secondary: 'bg-gray-200 text-black'
  };

  return (
    <button
      className={`px-4 py-2 rounded ${variantClasses[variant]}`}
      {...props}
    >
      {children}
    </button>
  );
}

2. TypeScript with Destructuring

// ✅ TypeScript with destructuring
interface UserProfileProps {
  user: {
    id: string;
    name: string;
    email: string;
    avatar?: string;
  };
  onEdit?: (id: string) => void;
  className?: string;
}

function UserProfile({
  user: { id, name, email, avatar = '/default-avatar.png' },
  onEdit,
  className = ''
}: UserProfileProps) {
  return (
    <div className={`profile-card ${className}`}>
      <img src={avatar} alt={name} />
      <h3>{name}</h3>
      <p>{email}</p>
      {onEdit && <button onClick={() => onEdit(id)}>Edit</button>}
    </div>
  );
}

3. Aliasing Destructured Properties

// ✅ Renaming destructured variables
function MapMarker({
  position: { lat: latitude, lng: longitude },
  label
}) {
  return (
    <div className="marker">
      <span>{label}</span>
      <span>({latitude}, {longitude})</span>
    </div>
  );
}

Best Practices

  1. Destructure at the parameter level when possible
  2. Provide default values for optional props
  3. Use rest parameters for props forwarding
  4. Keep destructuring readable – avoid too many nested levels
  5. Document prop types with TypeScript or PropTypes
  6. Consider component reusability when designing prop interfaces
// Well-structured component example
function DataTable({
  columns,
  data,
  loading = false,
  onRowClick,
  ...tableProps
}) {
  if (loading) return <LoadingSpinner />;

  return (
    <table {...tableProps}>
      <thead>
        <tr>
          {columns.map(({ header, key }) => (
            <th key={key}>{header}</th>
          ))}
        </tr>
      </thead>
      <tbody>
        {data.map((row) => (
          <tr key={row.id} onClick={() => onRowClick?.(row)}>
            {columns.map(({ key }) => (
              <td key={`${row.id}-${key}`}>{row[key]}</td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

Common Pitfalls to Avoid

  1. Over-destructuring complex objects (keep it readable)
  2. Ignoring prop types (use TypeScript or PropTypes)
  3. Forgetting default values for optional props
  4. Mutating props (props should be treated as immutable)
  5. Deeply nested destructuring that makes code hard to follow

Remember that proper prop destructuring:

  • Makes component APIs clearer
  • Reduces verbosity in your component code
  • Helps catch missing props early
  • Improves code maintainability
  • Works beautifully with TypeScript

Always aim for destructuring patterns that make your component’s interface obvious and your implementation clean.

Leave a Reply

Your email address will not be published. Required fields are marked *