Abstraindo as chamadas da aplicação para uma camada de serviços

Separando as responsabilidades de uma aplicação front end

Introdução

Salve pessoal! Cá estava eu, pensando em qual seria o próximo conhecimento relevante que eu poderia compartilhar com os colegas de profissão. Foi então que me veio em mente algo que eu gosto muito de exercitar quando estou codando, que é "separar as responsabilidades" do código ao máximo possível.

Bom, se você não se preocupa em pelo menos tentar manter um código bem organizado, então essa é a hora de começar a se preocupar com isso.

Motivação

Em um cenário real, é comum que uma aplicação faça chamadas externas para api's ou serviços para alimentar a view/app. Isso é algo que pode ser feito usando fetch, XML HTTP ou até mesmo um sdk.

Para facilitar o trabalho e evitar "repetição de código" é possível abstrair as chamadas externas do app em uma camada de serviços, onde podemos criar Promises que irão fazer o trabalho de buscar os dados e retornar para a view apenas aquilo que é necessário para a aplicação. 👌

export const getUserRepos = user => {
    return new Promise((resolve, reject) => {
        fetch(`https://api.github.com/users/${user}/repos`)
        .then(data => data.json())
        .then(data => {
            resolve(data);
        })
        .catch(error => {
            reject(error);
        });
    });
};

Isso evita de ter que repetir a url do fetch e o tratamento da resposta em cada lugar onde essa chamada seria necessária na aplicação, evitando repetir código e facilitando a implementação.

Tendo o serviço criado, basta chamar nos componentes onde ele vai ser útil e ser feliz.

async function handleSubmit(evt) {
    evt.preventDefault();

   if (loading) return;

   setLoading(true);

   try {
        const repos = await getUserRepos(inputValue);

        setUserRepos(repos);
    } catch (error) {
        alert(error);
    } finally {
        setLoading(false);
    }
}

Aqui está o código completo do componente onde foi usado o service:

import React, { useState } from "react";

import { getUserRepos } from "../services/user.service";

function UserPage() {
  const [inputValue, setInputValue] = useState("");
  const [loading, setLoading] = useState(false);
  const [userRepos, setUserRepos] = useState(null);

  /**
   * Set Input Value
   *
   * @param {object} evt
   */
  function handleInputValue(evt) {
    setInputValue(evt.target.value);
  }

  /**
   * Submit form
   *
   * @param {object} evt
   */
  async function handleSubmit(evt) {
    evt.preventDefault();

    if (loading) return;

    setLoading(true);

    try {
      const repos = await getUserRepos(inputValue);

      setUserRepos(repos);
    } catch (error) {
      alert(error);
    } finally {
      setLoading(false);
    }
  }

  return (
    <section>
      <form onSubmit={handleSubmit}>
        <div className="form-data">
          <input
            type="search"
            value={inputValue}
            onChange={handleInputValue}
            disabled={loading}
          />
        </div>
        <div className="form-data">
          <button type="submit" disabled={loading}>
            {loading ? "Carregando" : "Buscar"}
          </button>
        </div>
      </form>

      {userRepos && userRepos.length && (
        <ul>
          {userRepos.map((repo, i) => (
            <li key={i}>
              <p>
                <b>Repo:</b> {repo.full_name}
              </p>
              <a href={repo.html_url} target="blank">
                Github page
              </a>
            </li>
          ))}
        </ul>
      )}
    </section>
  );
}

export default UserPage;

Caso a resposta precise ser tratada antes de chegar para a view renderizar, é possível fazer isso dentro do próprio service, deixando a responsabilidade de buscar os dados e devolver os dados tratados exclusivamente para o service, evitando código desnecessário e facilitando a manutenção.

Conclusão

Aqui você pode ver a estrutura da aplicação que usei para o exemplo enquanto escrevia o artigo.

├── src
│   ├── pages
|   |   └── User.js
│   └── services
|       └── user.service.js
└── README.md

Você pode conferir o código produzido durante a escrita do artigo no meu code sandbox.

Dicas, sugestões, conselhos ou melhorias? Você pode entrar em contato comigo pelo meu email ou abrir uma PR no repositório aqui.

Comentários