Olá, estudante da Alura, tudo bem? Meu nome é Patrícia Silva.
Audiodescrição: Patrícia Silva se descreve como uma mulher branca. Tem cabelos cacheados escuros na altura dos ombros e olhos castanhos. Usa óculos de aro preto e camiseta bordô. Ela está em um ambiente iluminado com cores azul e lilás.
Estamos felizes em te encontrar em mais um curso. Este curso é sobre SOLID aplicado ao React e teremos a oportunidade de aplicá-lo no desenvolvimento front-end.
Utilizaremos um e-commerce pré-existente chamado UseDev. Trata-se de uma aplicação funcional, mas ao longo do curso, passaremos por cada componente, aplicando cada princípio do SOLID para melhorar a estrutura e a manutenção do código.
A aplicação tem três páginas diferentes: a página home, que lista as categorias e os produtos em promoções; a página de detalhes do produto; e a página de carrinho de compras.
Com o SOLID aplicado ao React, perceberemos como podemos transformar este projeto em uma aplicação robusta, modular e flexível, além de mais fácil de testar e entender. Isso não apenas ajudará na manutenção do código, mas também na evolução das funcionalidades ao longo do ciclo de vida do projeto.
Aplicar os princípios do SOLID não é apenas uma técnica, mas uma mentalidade. Para fazer tudo isso, é necessário ter conhecimento de React e TypeScript.
Vamos juntos transformar o UseDev em um projeto mais robusto, modular e escalável. Até já!
Já estamos com a aplicação UseDev aberta, clonada e rodando no navegador. Esta aplicação é um e-commerce que precisa de melhorias para escalar de forma fácil e barata. Se ela for escalável e robusta, será também mais econômica, pois conseguiremos colocar as funcionalidades em produção rapidamente.
Vamos começar pela base, que são os botões, já que é um elemento tão comum. Na página inicial, temos um botão "Ver as novidades" no hero banner e um botão de "Inscrever" na seção de newsletter.
Também tem um botão de "Adicionar ao carinho" na página de detalhes do produto. Inclusive, este botão recebe um ícone que não está bem alinhado - podemos corrigir isso logo mais. Por fim, na página do carrinho, também há alguns botões para "Continuar comprando" e "Ir para pagamento".
No VS Code, na pasta "src > components > Button", temos um componente de botão no arquivo index.tsx
, onde podemos aplicar o princípio da responsabilidade única, também conhecido como single responsability principal ou SRP.
Atualmente, o botão não adere a esse princípio, pois nas linhas 38 e 39, há duas tags <span>
: um para receber e renderizar um ícone e outro para receber e renderizar um texto e um children
.
O princípio diz que um botão deve ser bom em uma única tarefa, que é renderizar um texto. Portanto, vamos apagar essas tags e modificar o código para que ele lide apenas com o children
. Assim, o que passarmos para o botão será renderizado.
Na linha 34, percebemos que o CSS do botão não possui uma classe chamada button
, mas sim um seletor. Vamos limpar esse CSS. No className
, vamos remover o Styles.button
.
Também podemos limpar a propriedade onClick
, que é passada como parâmetro para o botão, para depois ser passada ao handleClick
. Não precisamos do handleClick
; podemos passar a propriedade onClick
diretamente para o evento onClick
.
Como removemos o ícone e o texto, podemos pagar text
e icon
das linhas 19 e 20, onde ficam os argumentos recebidos pelo botão.
Button/index.tsx
:
const Button = ({
children,
variant = "primary",
size = "medium",
onClick,
style,
...props
}: ButtonProps) => {
return (
<button
style={style}
className={classnames(Styles[variant], Styles[size])}
onClick={onClick}
{...props}
>
{children}
</button>
);
};
Agora, no type ButtonProps
, também podemos removemos o texto e o ícone.
type ButtonProps = {
style?: CSSProperties;
children?: ReactNode;
variant?: "primary" | "secondary";
size?: "small" | "medium" | "large"; // Define diferentes tamanhos
onClick: (e: MouseEvent<HTMLElement>) => void; // Manipulação de click adicional
};
No entanto, se verificamos a página do carrinho no navegador, apenas o botão que renderiza a string "Ok" funciona. Os outros, como "Ir para o pagamento" e "Continuar comprando", não têm mais rótulo.
Na home, só o corpo do botão "Ver as novidades" foi renderizado, enquanto o botão da newsletter está correto. Na página do produto, o botão "Adicionar ao carrinho" renderiza o rótulo, mas não o ícone.
Voltando ao VS Code, precisamos refatorar quem usa o botão para passar as propriedades corretas, pois mudamos sua assinatura. No menu lateral esquerdo, vamos fazer uma busca por Button
, clicando na opção "Search" (ou "Ctrl + Shift + F").
Começaremos modificando o arquivo index.tsx
da "Homepage". No <HeroBanner>
, encontramos uma tag <Button>
que possui a propriedade text
do botão. Vamos apagar o text
e passar o rótulo "Ver as novidades" como children, ou seja, entre as tags de abertura e fechamento do <Button>
.
HomePage/index.tsx
:
<HeroBanner
backgroundImage="https://raw.githubusercontent.com/gss-patricia/use-dev-assets/refs/heads/main/banner-seceos-tablet.png"
mainImage="https://raw.githubusercontent.com/gss-patricia/use-dev-assets/8df6d50256e4b270eb794ccbc0314baf2a656211/hero.png"
>
<Typography variant="h1">
Hora de abraçar seu{" "}
<span style={{ color: "#8fff24" }}>lado geek!</span>
</Typography>
<Button onClick={() => console.log("ver novidades")} size="large">
Ver as novidades!
</Button>
</HeroBanner>
A próxima página será a página do carrinho. Em CartPage/index.tsx
, há três botões.
Na tag <Button>
da linha 110, vamos remover a propriedade text
e passar o rótulo "Continuar comprando" como children. Na linha 114, faremos o mesmo com o rótulo "Ir para pagamento".
Na linha 64, o rótulo do botão "Excluir" já está sendo passado como children, portanto, não precisamos alterá-lo.
CartPage/index.tsx
:
<div className={Styles.cartActions}>
<Button onClick={handleRedirect} variant="secondary">
Continuar comprando
</Button>
<Button onClick={() => console.log("pagamento")}>
Ir para pagamento
</Button>
</div>
Na página de detalhes do produto, em ProductDetail.tsx
, o rótulo do botão "Adicionar ao carrinho" na linha 74 já está sendo passada corretamente.
Porém, não passamos mais a propriedade icon
. Para resolver isso, podemos copiar o ícone <AddCarrinhoIcon />
, apagar a propriedade icon
e passá-lo como filho. Dessa forma, botão encapsulará tanto o rótulo quanto o ícone.
ProductDetail.tsx
:
<div className={Styles.action}>
<Button onClick={handleAddToCart}>
<AddCarrinhoIcon />
Adicionar ao carrinho
</Button>
</div>
Na página de detalhes do produto no navegador, verificamos que o ícone já é renderizado junto com o rótulo no botão "Adicionar ao carrinho", mas não há espaçamento entre o rótulo e o ícone.
Para corrigir esse problema, acessamos o arquivo Button.module.css
. No seletor button
, abaixo da linha 12, vamos adicionar a propriedade gap
com 8px
.
Button.module.css
:
button {
font-weight: bold;
cursor: pointer;
transition: background-color 0.3s ease;
display: flex;
justify-content: center;
align-items: center;
border-radius: 32px;
border: 1px solid var(--roxo);
padding: 16px 24px;
font-size: 20px;
line-height: 24px;
gap: 8px;
}
Finalmente, podemos verificar se todos os botões estão sendo renderizados corretamente na página inicial, na página de detalhes do produto e no carrinho.
O princípio da responsabilidade única diz que um componente ou módulo deve fazer apenas o que foi destinado a fazer.
O botão estava se preocupando com tarefas fora de seu escopo, como renderizar ícones. Por exemplo, em uma casa, cada cômodo é desenhado para uma tarefa específica. Podemos dormir no quarto, cozinhar na cozinha, mas não cozinhar no banheiro.
Neste contexto, o botão deve apenas receber um filho. Se futuramente o ícone precisar estar em outra posição ou o espaçamento precisar ser alterado, seria confuso mudar o comportamento do botão. Por isso, dividimos o código em pequenas partes que fazem sua responsabilidade muito bem.
Cada princípio do SOLID é importante e se complementa. O Single Responsibility abre portas para que possamos implementar os outros princípios, mostrando como eles se conectam.
Da mesma forma que refatoramos o botão, removendo suas responsabilidades de renderizar o ícone, também refatoraremos o input.
Com o VS Code aberto, vamos acessar o código do input
no arquivo index.tsx
, dentro da pasta "src > components > Input". Observamos que o input
está realizando muitas funções.
Ele está renderizando o ícone, quando deveria ser apenas um campo de entrada. Portanto, podemos remover o icon
retornado na linha 35 e a propriedade icon
na linha 16, pois não serão necessários. Em InputProps
, também podemos apagar a tipagem do icon
na linha 5.
Na linha 1, importamos o ReactNode
, que não será mais utilizado, então podemos limpar o código.
Input/index.tsx
:
import { CSSProperties } from "react";
import Styles from "./Input.module.css";
type InputProps = {
variant?: "primary" | "secondary";
placeholder?: string;
value?: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
id?: string;
style?: CSSProperties;
type?: string;
};
const Input = ({
variant = "primary",
onChange,
placeholder,
id,
style,
type = "text",
...props
}: InputProps) => {
return (
<div className={`${Styles.inputContainer} ${Styles[variant]}`}>
<input
type={type}
style={style}
{...props}
onChange={() => onChange}
id={id}
placeholder={placeholder}
/>
</div>
);
};
export default Input;
Já fizemos a limpeza, mas há uma melhoria que podemos implementar. O input
é um componente compartilhado, ou seja, é usado em várias páginas por diversos componentes.
E na linha 24, temos uma <div>
com a classe inputContainer
. No CSS do input
, essa classe faz um posicionamento relative
. No entanto, isso não é uma boa prática, pois quem utiliza o input
deve controlar seu posicionamento: se é relativo, absoluto ou sem posicionamento.
Por isso, não usaremos essa div
e podemos removê-la. Mas, ainda queremos a variante do input
, que possui estilos primário e secundário. Então, copiamos o Styles[variant]
presente na div
e passamos um className
igual ao Styles[variant]
. Dessa forma, mantemos a variante.
const Input = ({
variant = "primary",
onChange,
placeholder,
id,
style,
type = "text",
...props
}: InputProps) => {
return (
<input
type={type}
className={Styles[variant]}
style={style}
{...props}
onChange={() => onChange}
id={id}
placeholder={placeholder}
/>
);
};
Vamos conferir o resultado da aplicação no navegador. No componente header
, o input já está diferente do estilo original proposto no Figma. O estilo original tem bordas e preenchimento em cinza-claro. Precisamos corrigir no CSS, já que movemos a variante que estava no contêiner da <div>
e a colocamos diretamente no <input>
.
Na linha 18 do arquivo Input.module.css
, vamos removendo a seleção direta dos elementos input
dentro das classes .primary
e .secondary
. Agora, os estilos aplicam-se ao próprio elemento que possui essas classes. Fazemos isso tanto para o estilo padrão, quanto para o hover
e o focus
de .primary
e .secondary
.
Além disso, não precisaremos mais do .inputContainer
e nem do .iconContainer
, pois não faz parte do escopo do input
. O estilo do ícone deve ser aplicado no próprio ícone.
Falta apenas adicionar o background
do campo de input
que deve ser um cinza-claro. Inclusive, já temos essas variáveis de cores prontas na aplicação. No estilo padrão de .secondary
, além da borda em --cinza-claro
, vamos acrescentar a propriedade background-color
com a mesma cor da borda.
Input.module.css
:
.primary:hover,
.secondary:hover {
border-color: var(--rosa);
}
.primary:focus,
.secondary:focus {
border-color: var(--rosa);
outline: none;
}
.primary {
background-color: transparent;
}
.secondary {
border-color: var(--cinza-claro);
background-color: var(--cinza-claro);
}
Agora, o estilo do input está correto, mas falta o ícone de lupa alinhado à direita do campo.
Sabemos que o input está sendo usado no componente header. Por isso, vamos acessar o diretório "src > components > Header" e abrir o index.tsx
. Na linha 51, o icon
está em vermelho, indicando que estamos passando a propriedade ícone, que não é mais necessária. Por isso, podemos apagá-la.
Precisamos usar o <SearchIcon>
no contêiner do <Input>
de pesquisa. Na linha 53, há um botão nativo do HTML que não deveria ser usado. Vamos usar o botão correto, que é o <Button>
.
Inclusive, já aplicamos o princípio da responsabilidade única no botão, portanto, ele trabalha com o recebimento de children. Isso significa que podemos passar um ícone através desse componente, ou seja, o <SearchIcon>
. Também importamos o componente Button
, que ainda não havia sido importado.
Na tag de abertura do <Button>
, vamos passar o onClick
, pois precisamos dele.
No searchContainer
, temos o Input
e o Button
. Como o botão aceita children, passando o ícone. Assim, o botão com o ícone é renderizado no navegador. Porém, o posicionamento está incorreto.
Para solucionar esse problema, vamos inspecionar esse botão com o DevTools com o atalho "Ctrl + Shift + C". No botão que contém o ícone de lupa, testamos o posicionamento relativo e uma margem direita de 36 pixels.
Depois de verificar que essa posição funcionou, podemos copiar o estilo CSS. Assim, já sabemos qual estilo passar para o botão no VS Code. Também sabemos que o botão aceita a propriedade de estilo, pois tinha a tipagem style
que aceita propriedades CSS. Nesse style
, passamos position
como relative
e right
como 36px
.
Header/Index.tsx
:
import Button from "../Button";
<div className={Styles.searchContainer}>
<Input
variant="secondary"
value={query}
onChange={handleInputChange}
placeholder="O que você procura?"
/>
<Button
style={{ position: "relative", right: "36px" }}
onClick={handleSearch}
>
<SearchIcon />
</Button>
</div>
No navegador, o ícone de lupa está renderizado corretamente. Inclusive, o botão é acessível e focável.
Também podemos passar por todas as páginas e conferir os campos de input, como o campo para digitar o endereço de e-mail na seção de newsletter e o campo para digitar o cupom na página do carrinho.
O input é um componente para receber entrada, sem responsabilidades adicionais. Com essa composição, quem o utiliza que deve se preocupar com seu posicionamento ou ícone.
O curso SOLID aplicado ao React: melhorando a modularidade e flexibilidade do código possui 161 minutos de vídeos, em um total de 46 atividades. Gostou? Conheça nossos outros cursos de React em Front-end, ou leia nossos artigos de Front-end.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.