S: Single Responsibility - One Pokémon, One Job
Problem: PokemonComponent
handles catching, battling, and displaying scores, violating SRP.
function PokemonComponent({ pokemon, onCatch, onBattle, score }) {
return (
<div>
<h2>{pokemon.name}</h2>
<button onClick={() => onCatch(pokemon)}>Catch</button>
<button onClick={() => onBattle(pokemon)}>Battle</button>
<div>Score: {score}</div>
</div>
);
}
Solution: Split responsibilities.
function PokemonCatcher({ pokemon, onCatch }) {
return <button onClick={() => onCatch(pokemon)}>Catch</button>;
}
function PokemonBattler({ pokemon, onBattle }) {
return <button onClick={() => onBattle(pokemon)}>Battle</button>;
}
function ScoreBoard({ score }) {
return <div>Score: {score}</div>;
}
function PokemonGame({ pokemon, onCatch, onBattle, score }) {
return (
<div>
<h2>{pokemon.name}</h2>
<PokemonCatcher pokemon={pokemon} onCatch={onCatch} />
<PokemonBattler pokemon={pokemon} onBattle={onBattle} />
<ScoreBoard score={score} />
</div>
);
}
O: Open/Closed - Evolving Pokémon Components
Problem: Adding features like power-ups requires modifying existing components.
Solution: Use a Higher-Order Component (HOC).
function withPowerUp(PokemonComponent) {
return function PoweredUpComponent(props) {
const [isPoweredUp, setPowerUp] = useState(false);
const powerUp = () => {
setPowerUp(true);
setTimeout(() => setPowerUp(false), 5000);
};
return (
<div>
<PokemonComponent {...props} isPoweredUp={isPoweredUp} />
<button onClick={powerUp}>Power Up!</button>
</div>
);
};
}
const Charmander = ({ isPoweredUp }) => (
<div>Charmander {isPoweredUp && "(Powered Up!)"}</div>
);
const PoweredCharmander = withPowerUp(Charmander);
function PokemonApp() {
return <PoweredCharmander />;
}
L: Liskov Substitution - The Interchangeable Pokémon
Problem: Swapping components causes issues.
Solution: Use a base component.
function BasePokemon({ attack, children }) {
return (
<div className="pokemon">
<div>Attack: {attack}</div>
{children}
</div>
);
}
function Pikachu({ attack }) {
return (
<BasePokemon attack={attack}>
<h2>Pikachu</h2>
</BasePokemon>
);
}
function Charizard({ attack }) {
return (
<BasePokemon attack={attack}>
<h2>Charizard</h2>
</BasePokemon>
);
}
function PokemonBattle() {
return (
<div>
<BasePokemon attack="Tackle">
<h2>Generic Pokémon</h2>
</BasePokemon>
<Pikachu attack="Thunderbolt" />
<Charizard attack="Flamethrower" />
</div>
);
}
D: Dependency Inversion - Depend on Abstractions
Problem: Components tightly coupled with data sources.
Solution: Use context for data injection.
const PokemonContext = createContext();
function Pikachu() {
const { attack } = useContext(PokemonContext);
}
<PokemonContext.Provider value={{ attack: "Thunderbolt" }}>
<Pikachu />
</PokemonContext.Provider>
Cheatsheet: SOLID Principles
Principle | Poké-Mantra | Trainer’s Tip |
---|---|---|
Single Responsibility | One Pokémon, one role. | Split complex components into focused ones. |
Open/Closed | Evolve without changing. | Use HOCs, render props for new features. |
Liskov Substitution | Components like Pokémon moves - interchangeable. | Ensure components can be used interchangeably. |
Dependency Inversion | Depend on abstractions, not concretes. | Use context or props for data management. |
Top comments (0)