Cancelar requisições Fetch em React useEffect

O useEffect é um poderoso hook para realizar efeitos em suas aplicações React usando a sintaxe de componentes em funções.

Ao retornar uma função dentro do useEffect estamos entrando na faze de limpeza do efeito.

Como mostra a documentação, em componentes de classe, usaríamos os ciclos de vida componentDidMount e componentWillUnmount:

class FriendStatus extends React.Component {
constructor(props) { ... }

O exemplo acima pode ser resumido em:

  • [ A ]: Ao montar o componente, criamos uma inscrição/escuta na API ChatAPI.subscribeToFriendStatus e iremos executar a função handleStatusChange para cada mudança
  • [ B ]: Quando o componente for removido, estamos retirando essa inscrição/escuta, para evitar problemas, como vazamento de memória (memory-leaks)

Assim como mostrado na documentação, usando useEffect, teríamos a seguinte sintaxe:

function FriendStatus(props) {
...
useEffect(() => {
function handleStatusChange(status) { ... }

Perceba que estamos retornando uma função em [ C ], ela será executada pelo React ao remover o componente, removendo corretamente (a declração de função function cleanup() {} é opcional, você pode retornar uma função de seta () => {}, para didática do exemplo, estou copiando a documentação do React).

Com esse conceito fresco em mente, vamos falar da Fetch API.

Fetch API

A interface retornada pela Fetch API nos permite utilizar o Abort API, onde podemos passar um controlador para a requisição e, se necessário, realizar o cancelamento da requisição.

Traduzindo isso para código, teríamos a seguinte sintaxe:

const controller = new AbortController();
const signal = controller.signal();

Não vamos discutir os detalhes do significado “requisição em execução”, porém, um ponto que vale a pena comentar é: tome cuidado ao cancelar/abortar requisições que não são GET, por exemplo, POST/PUT/DELETE.

Agora que sabemos como transformar nossa requisição Fetch, podemos ter o seguinte fluxo:

  • Dentro de um useEffect, criamos um AbortController
  • Passamos para nosso fetch o signal
  • Retornamos uma função de limpeza no useEffect e executamos o .abort() dentro dela

Teríamos a seguinte sintaxe:

useEffect(() => {
const controller = new AbortController();
const signal = controller.signal();

No exemplo acima, estamos cancelando nossa requisição toda vez que o efeito for executado.

Que tal um exemplo prático?

Colocando tudo junto

Utilizando a TheCatApi como serviço, iremos usar a API de paginação para navegar suas respostas.

Teremos o seguinte caso:

  • Começar na página 0 com 5 itens
  • Um botão para adicionar 1 a página
  • Um botão para subtrair 1 a página
  • Listar os resultados

O exemplo complete ficaria assim:

function App() {
let [state, setState] = React.useState({
status: "idle",
page: -1,
cats: [],
error: ""
});

Visualmente teríamos:

Ao clicar em -1 e +1 rapidamente, podemos ver as requisições canceladas na aba Network do DevTools do seu navegador:

Finalizando

Você pode encontrar o exemplo complete no meu CodeSandbox:

https://codesandbox.io/s/cancel-fetch-using-abort-api-ktvwz

Ao discutirmos qual seria a melhor opção para evitar uma quantidade absurda de requisições desnecessárias pelo clique do usuário, usar AbortController talvez não seja a melhor opção. As práticas atuais ainda são válidas.

Em outros casos onde a duplicação de requests pode acontecer ao montar/desmontar um componente, utilizar o AbortController pode ajudar no desempenho no lado do cliente.

Qualquer pergunta, estou no Twitter: https://twitter.com/oieduardorabelo

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store