Trabalhando com Service

Trabalhando com Services

Você se lembra daquela pastinha chamada services que existe dentro de src?

Ela é comumente usada para armazenar arquivos de Javascript (.js) que contem funções ou classes responsáveis por realizar:

  • Requisições HTTP
  • Manipulação de dados
  • Gerencimanto de dados

Ou qualquer outra lógica que você queira separar, ou não declarar dentro de um componente.

Por exemplo, durante a sua jornada como desenvolvedor ReactJS, haverá momentos em que você vai precisar:

  • Fazer uma requisição para uma API externa de modo a retornar algum dado,
  • Gerar um código único (hash) que vai ser usado em alguma parte do seu sistema,
  • Fazer uma chamada para uma biblioteca que está instalada dentro do seu projeto.

Ou qualquer outra operação que você possa imaginar.

Daí você tem duas alternativas, ou você cria essa lógica dentro do código do seu componente, ou você abstrai para um arquivo diferente.

Como desenvolvedor eu voto na alternativa da abstração, pois assim fica até mais fácil identificar o que cada arquivo faz, e em caso de erro ou adição de nova funcionalidade, você já sabe para qual arquivo olhar 🤓

O fato de criarmos uma pasta chamada services segue o padrão de separação de responsabilidade, onde diferentes partes do nosso código são organizadas em diretórios específicos.

E como vimos anteriormente em jornada anteriores:

A Pasta Components: Contém os componentes do ReactJS da aplicação, como botões, formulários, barras de navegação, etc.

A Pasta Pages: Contém as páginas da aplicação, onde cada arquivo geralmente corresponde a uma rota da aplicação.

A Pasta Services: Contém funções ou classes responsáveis por realizar chamadas de API, manipulação de dados ou outras operações relacionadas ao negócio da aplicação.

Quando fazemos o usa da pasta services, você pode criar módulos reutilizáveis que encapsulam a lógica de comunicação com a API.

Como é o caso de termos um serviço especifico para conter a lógica de autenticação de usuários, ou quem sabe, um serviço especifico para receber e tratar os dados advindos de um servidor back-end.

Dito isso, partiu aprender a criar nossos primeiros serviços 😉

Criando seu projeto de testes

Antes de começarmos, vamos criar um novo projeto em ReactJS dedicado a esta lição. No meu caso criei um novo projeto chamado de trabalhando-com-services:    

npx create-react-app trabalhando-com-services

Após isso, não se esqueça de fazer aquela limpeza do código, e também aquela organizada nas pastas do src (organização por funcionalidade), sem se esquecer da famosa pasta service heim rs 😁  

Após isso, vamos criar o nosso primeiro componente, chamado de MeuComponente:

MeuComponente > index.jsx:

const MeuComponente = () =>{
 return (
 <div>
 <h1>Meu Componente</h1>
 <p>Este é o meu componente</p>
 </div>
 );
}

export default MeuComponente;

Em seguida não se esqueça de importar o seu componente dentro do App.js:

import MeuComponente from './components/MeuComponente';

export default function App() {
 return (
 <div>
 <MeuComponente />
 </div>
 );
}

Feito isso, partiu utilizar nossos services 😎

Utilizando a pasta services

Para utilizar os services dentro da sua aplicação em ReactJS, basta criar e separar os arquivos necessários dentro da pasta services seguindo o seguinte passo a passo:

Passo 1) Crie a pasta services dentro do diretório de código-fonte do seu projeto, mais especificamente dentro da pasta src.

Passo 2) Dentro da pasta services, crie arquivos para cada serviço que você precisar. Por exemplo, apiService.js, authService.js, dataService.js, etc.

Passo 3) Dentro de cada arquivo de serviço, defina as funções ou classes necessárias para realizar as operações desejadas.

Por exemplo, uma função para fazer uma requisição HTTP usando o fetch API ou uma classe que encapsula métodos para manipular dados locais.

Passo 4) Importe os serviços necessários nos componentes ou páginas onde você precisar utilizá-los.

Passo 5) Utilize os métodos dos serviços conforme necessário em sua aplicação.

Agora vamos ver alguns exemplos da sua utilização.

Exemplo 1) Separando a consulta de nomes de uma API em um service.

Vamos supor que no MeuComponente eu tenha criado uma lógica capaz de retornar uma lista de nomes de uma API externa de modo a mostrar os resultados na tela do usuário:

MeuComponente > index.jsx:

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

const MeuComponente = () => {
 const [nomes, setNomes] = useState([]);

 useEffect(() => {
 const fetchNomes = async () => {
 try {
 const response = await fetch('https://randomuser.me/api/?results=10');
 if (response.ok) {
 const data = await response.json();
 const nomes = data.results.map(result => `${result.name.first} ${result.name.last}`);
 setNomes(nomes);
 } else {
 throw new Error('Erro ao buscar nomes da API');
 }
 } catch (error) {
 console.error(error);
 }
 };

 fetchNomes();

 return () => {
 
 };
 }, []);

 return (
 <div>
 <h1>Meu Componente</h1>
 <p>Este é o meu componente</p>
 <ul>
 {nomes.map((nome, index) => (
 <li key={index}>{nome}</li>
 ))}
 </ul>
 </div>
 );
}

export default MeuComponente;

Veja como ficou o resultado final:

Como podemos ver no código acima, estamos fazendo o uso de alguns hooks como useState e useEffect, onde um deles armazena toda a lógica de chamada da API externa.

O problema é que se tivéssemos mais de um componente fazendo a mesma chamada para a mesma API, qualquer atualização por parte da API nos obrigaria a fazer alterações em todos esses componentes.

Mas porque fazer isso, se nós podemos abstrair essa lógica para um service?

Fazendo com que qualquer alteração ou erro por parte da API, nós precisássemos apenas focar na manutenção de um único arquivo?

Para isso basta criar um novo arquivo chamado de nomeService.js:

// Função para buscar nomes na API
export const fetchNomes = async () => {
 try {
 // Faz a requisição para a API
 const response = await fetch('https://randomuser.me/api/?results=10');
 // Verifica se a requisição foi bem sucedida
 if (response.ok) {
 // Converte a resposta para JSON
 const data = await response.json();
 // Extrai os nomes dos dados obtidos da API
 const nomes = data.results.map(result => `${result.name.first} ${result.name.last}`);
 // Retorna os nomes obtidos da API
 return nomes;
 } else {
 // Se a resposta da API não for bem sucedida, lança um erro
 throw new Error('Erro ao buscar nomes da API');
 }
 } catch (error) {
 console.error(error);
 // Em caso de erro, retorna uma lista vazia
 return [];
 }
};

Por fim, basta atualizar o MeuComponente para que ele importe esse service para começar a usá-lo:

import React, { useState, useEffect } from 'react';
import { fetchNomes } from '../../services/nomeService'; // Importa a função de busca de nomes

const MeuComponente = () => {
 const [nomes, setNomes] = useState([]);

 useEffect(() => {
 const fetchNomesFromAPI = async () => {
 const nomesObtidos = await fetchNomes();
 setNomes(nomesObtidos);
 };

 fetchNomesFromAPI();

 return () => {

 };
 }, []);

 return (
 <div>
 <h1>Meu Componente</h1>
 <p>Este é o meu componente</p>
 <ul>
 {nomes.map((nome, index) => (
 <li key={index}>{nome}</li>
 ))}
 </ul>
 </div>
 );
}

export default MeuComponente;

Exemplo 2) Separando a lógica de um aplicativo de tarefas

Dessa vez vamos criar um novo componente chamado de ListComponent que vai conter uma mini aplicação de gestão de lista de tarefas.

ListComponent > index.jsx:

import React, { useState, useEffect } from 'react';
import ListService from '../../services/listService';

const listService = new ListService();

const ListComponent = () => {
 const [list, setList] = useState(listService.getList());

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

 const loadList = () => {
 setList(listService.getList());
 }

 const addNewItem = () => {
 listService.addNewItem();
 loadList();
 }

 return(
 <div>
 <h2>List of Items:</h2>
 {list.map((item, index) => (
 <div key={index}>
 <h3>{item.title}</h3>
 </div>
 ))}
 <button onClick={addNewItem}>Add new Item</button>
 </div>
 )
}

export default ListComponent;

listService.js:

class ListService {
 constructor() {
 this.list = [
 { title: 'Item 1' },
 { title: 'Item 2' },
 { title: 'Item 3' }
 ];
 }

 getList(){
 return [...this.list];
 }

 addNewItem(){
 const newList = [...this.list, { title: `Item ${this.list.length + 1}` }];
 this.list = newList;
 }
}

export default ListService;

Veja como ficou o resultado final:

Note que no exemplo acima nós estamos fazendo uma coisa bastante peculiar que ainda não vimos durante a nossa jornada: O instanciamento de uma classe dentro de um useState.

const [list, setList] = useState(listService.getList());

Na próxima aula falaremos um pouco mais sobre o assunto Mitigando Estados com ReactJS 😉

Arquivos da lição

Os arquivos dessa lição podem ser encontrados neste repositório do GitHub.

Conclusão

Nesta lição você aprendeu um pouco mais sobre o tipo de conteúdo que deve existir dentro da pasta services, onde aprendeu a criar e gerenciar seus serviços, de modo a abstrair parte da lógica dos seus componentes para dentro de arquivos separados.

Te espero na próxima lição 😄