DEV Community

Cover image for Building a SOLID Pokémon Game in React: A Developer’s Adventure!
vigneshiyergithub
vigneshiyergithub

Posted on • Edited on

Building a SOLID Pokémon Game in React: A Developer’s Adventure!

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> ); } 
Enter fullscreen mode Exit fullscreen mode

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> ); } 
Enter fullscreen mode Exit fullscreen mode

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 />; } 
Enter fullscreen mode Exit fullscreen mode

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>  ); } 
Enter fullscreen mode Exit fullscreen mode

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> 
Enter fullscreen mode Exit fullscreen mode

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)