Embora tenha sido concebido para a programação orientada a objetos, os princípios do SOLID podem ser aplicados em outros contextos como em componentes React.
Este post aborda o "S" do [S]OLID
Criado com Next 13 + React 18
Codepen no final do post
SRP - Single Responsibility Principle
O Single Responsibility Principle (Princípio da Responsabilidade Única) é um dos cinco princípios do SOLID, que é uma abordagem para escrever código limpo, modular e escalável. O princípio SRP estabelece que uma classe ou módulo deve ter apenas uma razão para mudar, ou seja, deve ter apenas uma responsabilidade.
No código abaixo, o componente está lidando com a obtenção de dados da API, o gerenciamento do estado do componente e a estilização do componente. Isso torna o componente difícil de manter e entender, pois tem muitas responsabilidades.
/**
* components
* examples
* solid
* srp
* ListUsersAfter
* index.tsx
*/
// srp/ListUsersAfter/index.tsx
'use client';
import axios from 'axios';
import { useEffect, useState } from 'react';
type User = {
name: {
first: string;
};
email: string;
gender: 'male' | 'female';
};
type Data = {
results: User[];
};
export const ListUsersBefore = () => {
const [data, setData] = useState<Data>();
const [isLoading, setIsLoading] = useState(false);
const handleFetch = async () => {
setIsLoading(true);
const url = 'https://randomuser.me/api/?results=5';
const res = await axios.get(url);
setIsLoading(false);
setData(res.data);
};
useEffect(() => {
handleFetch();
}, []);
return (
<>
{isLoading && <div>Loading...</div>}
{data?.results?.map((user) => (
<div
key={user.email}
style={{
color: user.gender === 'male' ? 'blue' : 'red',
border: '1px solid',
maxWidth: '200px',
padding: '0.2rem',
marginBottom: '0.2rem',
borderRadius: '4px'
}}
>
{user.name.first}
</div>
))}
</>
);
};
Para resolver esse problema e aderir ao princípio SRP, podemos dividir o componente em vários componentes menores e mais focados em suas responsabilidades.
/**
* components
* examples
* solid
* srp
* ListUsersAfter
* hooks
* useUsers
* index.ts
* index.tsx
* styles.ts
*/
O estilo que antes estava dentro do componente principal foi removido e isolado em outro componente.
//srp/ListUsersAfter/styles.ts
import styled from 'styled-components';
type StyledUserProps = {
gender: 'male' | 'female';
};
export const S = {
Div: styled.div<StyledUserProps>`
color: ${(props) => (props.gender === 'male' ? 'blue' : 'red')};
border: 1px solid;
max-width: 200px;
padding: 0.2rem;
margin-bottom: 0.2rem;
border-radius: 4px;
`
};
Agora, o componente principal tem apenas a responsabilidade de renderizar a lista de usuários.
//srp/ListUsersAfter/index.tsx
import { useUsers } from './hooks/useUsers';
import { S } from './styles';
export const ListUsersAfter = () => {
const { data, isLoading } = useUsers();
return (
<>
{isLoading && <div>Loading...</div>}
{data?.map((user) => (
<S.Div gender={user.gender} key={user.email}>
{user.name.first}
</S.Div>
))}
</>
);
};
A responsabilidade de adquirir os dados foi isolada em um componente na pasta hooks. Além disso, houve melhorias nas tipagens, a aplicação da técnica debounceTime e o tratamento de erros, embora esta última técnica não faça parte do SOLID.
//srp/ListUsersAfter/hooks/useUsers/index.ts
import { sortObjects } from '@/app/utils/sortObjects';
import axios from 'axios';
import { useCallback, useEffect, useState } from 'react';
type User = {
name: {
first: string;
};
email: string;
gender: 'male' | 'female';
};
export type useUsersProps = {
debounceTime?: number;
sortOrder?: 'asc' | 'desc';
limit?: number;
};
type useUsersReponse = {
data: User[];
isLoading: true | false;
error: null | string;
};
export const useUsers = ({
debounceTime = 500,
sortOrder = 'asc',
limit = 5
}: useUsersProps = {}): useUsersReponse => {
const [data, setData] = useState<User[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<null | string>(null);
const handleFetch = useCallback(async () => {
setIsLoading(true);
try {
const url = `https://randomuser.me/api/?results=${limit}`;
const res = await axios.get(url);
setData(sortObjects(res.data.results, 'name.first', sortOrder));
setError(null);
} catch (err) {
setError('Error get Users!');
}
setIsLoading(false);
}, [sortOrder, limit]);
useEffect(() => {
const timer = setTimeout(() => {
handleFetch();
}, debounceTime);
return () => {
clearTimeout(timer);
};
}, [debounceTime, handleFetch]);
return {
data,
isLoading,
error
};
};
Devido à limitação da plataforma, todo o exemplo está contido em um único arquivo, sem a possibilidade de separação em pastas e arquivos distintos. Entretanto, ao longo do post, as responsabilidades serão abordadas em camadas distintas.
Top comments (0)