SOLID em React: Entendendo o Single-Responsibility Principle (SRP)

Vinicios Neves
5 min readJun 12, 2023

Salve, pessoa!

Aqui estamos nós, trilhando o caminho inverso no SOLID, agora chegando no primeiro princípio. Ou seja, começamos do último e fomos rebobinando a fita, como nos velhos tempos de VHS (eu sei, eu sei, essa referência revela idade 👴), até chegarmos ao primeiro. Afinal, quem disse que precisa ser sempre da esquerda pra direita, né?

Brincadeiras à parte, hoje vamos falar sobre o Single-Responsibility Principle (Princípio da Responsabilidade Única), ou simplesmente SRP.

O que é o Single-Responsibility Principle (SRP)?

O SRP é um princípio que vem do universo da Orientação a Objetos, criado por Robert C. Martin. Ele diz que uma classe deve ter apenas uma responsabilidade, ou seja, deve ter apenas um motivo para mudar.

Para nós, que trabalhamos com React, podemos traduzir isso como: um componente deve ter apenas uma responsabilidade, apenas uma razão para mudar.

É como se cada componente fosse um membro de uma banda. O vocalista não deve tocar a bateria, o guitarrista não deve ser o baixista, e assim por diante. Cada um tem a sua função, a sua responsabilidade. E isso não só facilita a vida de cada um, como também torna a banda (ou o nosso aplicativo) muito mais harmoniosa.

Você pode ter pensado: mas Vinny, tem bandas onde uma pessoa tem mais de uma função, como tocar e cantar por exemplo. Excelente! Como tenho tentado evidenciar, os princípios são um guia e não a verdade universal. Podemos ter cenários onde temos um vocalista que é um excelente guitarrista e seria um prejuízo não aproveitar essas qualidades.

Entendendo na prática

Imagina um componente que lista naves, baseado na Star Wars API (https://swapi.dev/documentation#starships).

import React, { useState, useEffect } from 'react';
import axios from 'axios';

const App = () => {
const [filtroNome, setFiltroNome] = useState('');
const [filtroModelo, setFiltroModelo] = useState('');
const [naves, setNaves] = useState([]);

useEffect(() => {
const obterNaves = async () => {
const resposta = await axios.get(`https://swapi.dev/api/starships/?search=${filtroNome}${filtroModelo}`);
setNaves(resposta.data.results);
};

obterNaves();
}, [filtroNome, filtroModelo]);

return (
<div>
<form>
<input
type="text"
placeholder="Nome da nave"
onChange={e => setFiltroNome(e.target.value)}
/>
<input
type="text"
placeholder="Modelo da nave"
onChange={e => setFiltroModelo(e.target.value)}
/>
</form>

{naves.map(nave => (
<div key={nave.name}>
<h2>{nave.name}</h2>
<p>{nave.model}</p>
</div>
))}
</div>
);
};

export default App;

No exemplo acima, o componente App é responsável por três tarefas principais:

  1. Buscar os dados da API (dentro do useEffect através da função obterNaves).
  2. Lidar com os filtros de nome e modelo (usando o estado React useState e os elementos input no retorno do componente).
  3. Renderizar a lista de naves obtidas (no retorno do componente, mapeando sobre o estado naves).

Componentes com muitas responsabilidades podem facilmente crescer desordenadamente e virar uma bagunça. Quando isso acontece e a gente vai mexer nele o processo fica tão delicado e com tantas linhas de código que eventualmente a gente fica perdido.

Se a gente tentar aplicar os principios da responsabilidade única, poderíamos ter algo mais ou menos assim:

const useNaves = (filtroNome, filtroModelo) => {
const [naves, setNaves] = useState([]);

useEffect(() => {
const obterNaves = async () => {
const resposta = await axios.get(`https://swapi.dev/api/starships/?search=${filtroNome}${filtroModelo}`);
setNaves(resposta.data.results);
};

obterNaves();
}, [filtroNome, filtroModelo]);

return naves;
};
const FormularioBusca = ({ setFiltroNome, setFiltroModelo }) => {
return (
<form>
<input
type="text"
placeholder="Nome da nave"
onChange={e => setFiltroNome(e.target.value)}
/>
<input
type="text"
placeholder="Modelo da nave"
onChange={e => setFiltroModelo(e.target.value)}
/>
</form>
);
};
const ListaNaves = ({ naves }) => {
return (
<div>
{naves.map(nave => (
<div key={nave.name}>
<h2>{nave.name}</h2>
<p>{nave.model}</p>
</div>
))}
</div>
);
};
const App = () => {
const [filtroNome, setFiltroNome] = useState('');
const [filtroModelo, setFiltroModelo] = useState('');
const naves = useNaves(filtroNome, filtroModelo);

return (
<div>
<FormularioBusca setFiltroNome={setFiltroNome} setFiltroModelo={setFiltroModelo} />
<ListaNaves naves={naves} />
</div>
);
};

Agora nós temos:

  • FormularioBusca: captura e manipula as entradas do usuário para os filtros de nome e modelo.
  • useNaves: realiza a busca das naves na API Star Wars com base nos filtros de nome e modelo.
  • ListaNaves: exibe a lista de naves buscadas.
  • App: integra todos os componentes e mantém o estado dos filtros de nome e modelo.

Eu não sei você, mas essas porções menores de código com responsabilidade bem definida acalmam e aquecem o meu coração :)

This is the end

Finalizamos a nossa jornada pelo SOLID. Percorremos os cinco princípios, sempre com o objetivo de aprender como eles podem nos ajudar a escrever um código melhor no React.

Lembre-se de que, assim como na vida, desenvolver software é uma caixinha de surpresas. SOLID não é uma receita de bolo que deve ser seguida à risca. É um conjunto de diretrizes que podem nos ajudar a tomar decisões mais inteligentes.

Relembrando nossa jornada:

  1. Single-responsibility principle (SRP) [Você está aqui]
  2. Open-closed principle (OCP)
  3. Liskov substitution principle (LSP)
  4. Interface segregation principle (ISP)
  5. Dependency inversion principle (DIP)

Agradeço a você que me acompanhou nessa jornada. Espero que tenha sido tão enriquecedora para ti quanto foi para mim. Sinta-se à vontade para deixar seus comentários, dúvidas e sugestões de novos temas. Afinal, estamos aqui para aprender juntos.

Grande abraço e até a próxima!

--

--