Liskov Substitution Principle (LSP)
REACT NATIVE HUB- Definition: Objects or components should be replaceable with instances of their subtypes without altering the correctness of the program.
- In React: When creating components or extending them, ensure they can be used interchangeably without breaking the application.
Not following LSP: In this example, incorrect use of interface methods and not following the base interface for the same type of component violates the LSP principle.
// ❌ BAD EXAMPLE - Violating LSP
interface BadBaseButton {
onClick: () => void;
text: string; // Restricts to string only
}
const BadBaseButton: React.FC<BadBaseButton> = ({ onClick, text }) => (
<button onClick={onClick}>{text}</button>
);
// Violates LSP by changing prop structure and behavior
interface BadPrimaryButton {
label: string; // Different prop name
onAction: (e: React.MouseEvent) => Promise<void>; // Different type
}
const BadPrimaryButton: React.FC<BadPrimaryButton> = ({ label, onAction }) => {
const handleClick = async (e: React.MouseEvent) => {
await onAction(e);
};
return <button onClick={handleClick}>{label.toUpperCase()}</button>;
};
// Usage shows incompatibility
const BadExample: React.FC = () => {
const handleClick = () => console.log('clicked');
return (
<div>
<BadBaseButton
onClick={handleClick}
text="Click me"
/>
{/* Won't work! Different props and behavior */}
<BadPrimaryButton
label="Click me"
onAction={async (e) => console.log('clicked')}
/>
</div>
);
};
Following LSP: It helps to ensure base props are extended consistently, the same onClick type throughout, components are interchangeable & add features without breaking the base contract.
// ✅ GOOD EXAMPLE - Following LSP
interface GoodBaseButtonProps {
onClick: () => void;
children: React.ReactNode;
}
const GoodBaseButton: React.FC<GoodBaseButtonProps> = ({ onClick, children }) => (
<button onClick={onClick}>{children}</button>
);
// Properly extends base props
interface GoodPrimaryButtonProps extends GoodBaseButtonProps {
variant?: 'solid' | 'outline'; // Optional additional prop
}
const GoodPrimaryButton: React.FC<GoodPrimaryButtonProps> = ({
onClick,
children,
variant = 'solid'
}) => {
const className = variant === 'solid' ? 'bg-blue-500' : 'border-blue-500';
return (
<button onClick={onClick} className={className}>
{children}
</button>
);
};
// Usage shows compatibility
const GoodExample: React.FC = () => {
const handleClick = () => console.log('clicked');
return (
<div>
{/* Both components can use the same handler */}
<GoodBaseButton onClick={handleClick}>
Click me
</GoodBaseButton>
<GoodPrimaryButton onClick={handleClick} variant="solid">
Click me
</GoodPrimaryButton>
</div>
);
};