SOLID in React- OCP - Open-Closed Principle
Tags: solidjs, react, nextjs
Published: 2023-04-29 01:44:57

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 "O" do S[O]LID
- Criado com Next 13 + React 18
- Codepen no final do post
- A aplicação inicia com o SRP aplicado

## OCP - Open-Closed Principle

O Open Closed Principle (Princípio Aberto/Fechado) preconiza que uma entidade de software (classe, módulo, função, etc.) deve estar aberta para extensão (ou seja, deve ser possível adicionar novos comportamentos ou funcionalidades sem modificar o código existente) e fechada para modificação (ou seja, o código existente não deve ser modificado para adicionar novos comportamentos ou funcionalidades).

Suponha que temos uma lista de usuários que podem ser exibidos em um componente em nossa aplicação. Inicialmente, exibimos apenas o primeiro nome e o gênero do usuário com um ícone ao lado do nome, sendo azul para usuários masculinos e vermelho para usuários femininos.

No contexto de componentes, o princípio Aberto/Fechado não está sendo aplicado ao código fornecido. Isso ocorre porque o componente ListUsersBefore precisa ser alterado se houver a necessidade de adicionar um filtro de usuários, componentes ou propriedades dentro dele. Isso viola o princípio, pois o componente deve estar aberto para extensão e fechado para modificação.

```
/**
 * ocp
 *  ListUsersBefore
 *    hooks
 *      useUsers
 *        index.ts
 *    index.tsx
 *    styles.ts
 */
```

Componente principal:

```tsx
//ocp/ListUsersBefore/index.tsx

import { FaFemale, FaMale } from 'react-icons/fa';
import { useUsers } from './hooks/useUsers';
import { S } from './styles';

export const ListUsersBefore = () => {
  const { data, isLoading } = useUsers();
  
  return (
    <>
      {isLoading && <div>Loading...</div>}

      {data.map((user) => (
        <S.Div 
          gender={user.gender} 
          key={user.email}
        >
          {(user.gender === 'female' && <FaFemale />) || <FaMale />}
          <span>{user.name.first}</span>
        </S.Div>
      ))}
    </>
  );
};

```
Estilos do componente principal: 

```ts
//ocp/ListUsersBefore/styles.ts

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;
  `
};
```

Hook que fornece os dados:

```ts
//ocp/ListUsersBefore/hooks/useUsers/index.tsx

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

type User = {
  name: {
    first: string;
  };
  email: string;
  gender: 'male' | 'female';
};

type useUsersReponse = {
  data: User[];
  isLoading: true | false;
  error: null | string;
};

export const useUsers = (): useUsersReponse => {
  const [data, setData] = useState<User[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<null | string>(null);

  const handleFetch = async () => {
    setIsLoading(true);

    try {
      const url = `https://randomuser.me/api/?results=5`;
      const res = await axios.get(url);

      setError(null);
      setData(res.data.results);
    } catch (err) {
      setError('Error get Users!');
    }

    setIsLoading(false);
  };

  useEffect(() => {
    handleFetch();
  }, []);

  return {
    data,
    isLoading,
    error
  };
};

```