React Query(useQuery e useMutation) - O que, como e quando
Published: 2023-04-27 01:27:42


O React Query é uma biblioteca de busca de dados que ajuda a buscar, armazenar em cache, sincronizar e atualizar o estado do servidor em seu aplicativo react. Antes de começarmos a entrar nos detalhes do React Query, é essencial entender o estado do servidor.

Nota: Este artigo requer uma compreensão intermediária da criação de aplicativos do lado do cliente com o React.

A maioria dos desenvolvedores com experiência em React entende o que é estado. Para entender o estado do servidor, vamos listar as diferenças entre o estado do cliente e do servidor.

Estado do Cliente

Os dados são armazenados no navegador.
O usuário pode alterar os dados localmente.
Os dados ficam na memória e são perdidos em recargas.
Estado do Servidor

Os dados são armazenados em um servidor remoto.
A alteração de dados requer acesso ao servidor remoto ou APIs.
Os dados persistem em um banco de dados remoto.
O React Query só ajuda a gerenciar o estado do servidor.

Com o que o React Query pode ajudar?
1. O Caching
React Query fica entre o servidor e o cliente interceptando todas as consultas. Cada consulta é vinculada a uma chave exclusiva chamada chave de consulta no repositório de consultas react. Quando você tenta rebuscar dados para a mesma chave de consulta, ele retorna imediatamente os dados armazenados em cache.

2. Identificando e atualizando dados obsoletos em segundo plano
Quando os dados são buscados no servidor, ele os compara aos dados armazenados em cache. Se ambos forem iguais, isso não forçará uma recarga. O React Query também garante que o cache seja atualizado fazendo solicitações de busca em segundo plano para manter os dados em sincronia.

3. Gerenciamento de memória e coleta de lixo O React Query possui um coletor de
lixo para gerenciar o cache no navegador. Se os dados no cache não forem consumidos, eles serão excluídos após um tempo limite. Esse tempo limite pode ser configurado localmente para cada consulta ou globalmente. Isso ajuda a reter apenas as informações mais relevantes enquanto limpa o restante do cache.

4. Desviando várias solicitações para os mesmos dados
Se várias solicitações para a mesma chave de consulta forem feitas próximas umas das outras, apenas a primeira será enviada por fio. Todas as outras solicitações serão resolvidas com os dados da primeira. Isso economiza largura de banda e melhora a experiência do usuário.

5. Otimizações
de desempenho O React Query tem suporte pronto para uso para otimizações de renderização, paginação, carregamento lento, etc., para melhorar o desempenho do seu aplicativo.

Quando você deve usar o React Query?
O React Query é uma ferramenta. Para obter o seu máximo benefício, é essencial saber quando usá-lo. Vamos entender isso com dois exemplos

Um aplicativo de mídia social: os usuários podem visualizar linhas do tempo / postagens e conversar com amigos. O de sempre.
Um aplicativo de produção musical: Um primeiro aplicativo off-line onde os usuários podem criar música com diferentes instrumentos.
O React Query seria mais adequado para o aplicativo de mídia social. Este aplicativo se integraria naturalmente com várias APIs, exigindo assim o gerenciamento do estado do servidor. Este aplicativo também teria seu estado de cliente, portanto, uma biblioteca como o React Query permitirá que você gerencie bem o estado do servidor.

O React Query tornou-se popular porque a maioria dos aplicativos tem uma ou outra forma do componente do servidor. À medida que o número de APIs aumenta, faz mais sentido usar essa biblioteca.

Aqui estão algumas dicas sobre por que o React Query é uma escolha digna:

Simplifica a busca de dados - Apaga muito clichê e facilita a leitura do seu código.
Fornece ferramentas para melhorar a confiabilidade dos dados do servidor - Invalida e marca os dados como obsoletos.
Fornece ferramentas para melhorar a experiência do usuário - pré-busca, rebusca, armazenamento em cache e muito mais.
Otimizações de desempenho - Paginação e carregamento lento.
Solicitar novas tentativas - Capacidade de repetir em caso de erros.
Rebusca de foco de janela - Rebusca com base na atividade da guia do aplicativo.
O React Query substitui o Redux, o MobX ou outras bibliotecas globais de gerenciamento de estado?
Redux, MobX e as outras bibliotecas populares são ótimas para o gerenciamento do estado do cliente. O React Query complementa essas bibliotecas e ajuda a criar uma separação de preocupações. Na maioria dos aplicativos do lado do cliente, eles lidam com o estado do servidor. Eles normalmente executam estas etapas:

Buscar dados do servidor
Armazenar os dados do servidor no repositório do cliente
Forneça uma mídia para que os componentes se comuniquem e acessem os dados do armazenamento.
O React Query ajuda você a abstrair as funções acima. Portanto, deixando apenas o estado real do cliente para ser armazenado usando bibliotecas de gerenciamento de estado do cliente. É por isso que você fica com o pouco estado a ser mantido ao migrar para o React Query.

Armadilhas ao trabalhar com o React Query
1. Tamanho de pacote grande O React
Query tem um impacto no tamanho do aplicativo. É por causa de todos os recursos que vem com ele. O tamanho do pacote grande pode afetar seu desempenho. Isso pode causar atrasos durante o carregamento, renderização e interação do usuário. Para dar mais contexto, de acordo com o BundlePhobia o React Query é cerca de 3 vezes maior que um de seus concorrentes SWR. A ideia aqui não é assustá-lo pelo tamanho do pacote, em vez disso, ajudá-lo a verificar se ele é perfeito para sua aplicação.

_React Query bundle size as reported by bundle phobia._Tamanho do pacote React Query, conforme relatado pela fobia do pacote.

_SWR bundle size as reported by bundle phobia._Tamanho do feixe SWR conforme relatado pela fobia do feixe.

2. Identificando chaves de consulta para invalidar
o React Query fornece um gancho useMutation para atualizar dados em seu servidor. Se a atualização para o servidor passar, Ele fornece a função de retorno de chamada onSuccess e onMutate para atualizar o cache no repositório de consulta react. A armadilha aqui é que, para cada atualização feita no servidor, uma etapa manual é necessária para identificar e invalidar as chaves de consulta. Vamos entender por que isso fica complicado:

Identificar todas as chaves de consulta no repositório do React Query relacionadas à atualização que passou é difícil. Há um alto potencial de que você pode perder na identificação dessas chaves em grandes aplicativos. É propenso a erros e uma boa compreensão da plataforma é necessária para evitar essa armadilha.
As chaves de consulta geralmente são invalidadas com a ajuda da chamada invalidateQueries. As chaves de consulta que você passa para a chamada invalidateQueries devem corresponder à chave de consulta que foi definida inicialmente. Se a chave de consulta for uma matriz de Strings, a ordem em que os parâmetros são passados na chave de consulta também deverá ser a mesma.
3. Identificação de chaves de consulta para cache O React
Query requer chaves de consulta apropriadas para que o cache funcione conforme o esperado. Defini-los pode ficar complicado às vezes. Para entender melhor, vamos considerar um exemplo. Digamos que você precise buscar a contagem de todos os usuários que chegaram à sua plataforma desde o início do ano. A consulta para buscar a contagem de usuários depende da data de início e da data de término. Esses parâmetros devem fazer parte da chave de consulta, conforme mostrado abaixo
[
  'usersCount',
  { startDate: '2022-01-01+00:00:00', end_date: '2022-12-08+00:53:56' }
];
Digamos que eu vim depois de um minuto e solicitei para a contagem de usuários desde o início do ano, ele não armazena em cache. Em vez disso, ele solicita diretamente do servidor. Este não deve ser o comportamento esperado. Idealmente, com tudo o que aprendemos, ele deveria ter retornado do cache enquanto buscava as atualizações mais recentes em segundo plano. Para entender isso, vamos estruturar a chave de consulta para a solicitação que foi feita após um minuto.
[
  'usersCount',
  { startDate: '2022-01-01+00:00:00', end_date: '2022-12-08+00:54:56' }
];
Você notaria que a data de término foi alterada. Essa chave não está correspondendo com a antiga que está presente no cache. Então ele busca diretamente do servidor.

Para corrigir esse problema, os parâmetros definidos na chave de consulta precisam ser modificados. Em vez de considerar todo o carimbo de data/hora, e se considerarmos apenas a data na chave de consulta. É importante observar que os parâmetros que são passados para a chamada de API ainda permanecem inalterados. Ainda enviamos todo o carimbo de data/hora. É somente na chave de consulta que a modificação é feita, conforme mostrado abaixo.
[
  'usersCount',
  { startDate: '2022-01-01', end_date: '2022-12-08' }
];
Se você voltasse depois de um minuto e fizesse uma solicitação, ela seria buscada instantaneamente no cache. Isso ocorre porque sua chave de consulta permanece inalterada e corresponde à do cache. Mas o que é interessante aqui é que a busca em segundo plano é para os carimbos de data/hora solicitados do servidor. Uma vez que as atualizações mais recentes estão disponíveis, ele as pinta na seção de contagem de usuários.

Busca de dados no React Query
A busca de dados é um efeito colateral muito comum que geralmente é gerenciado com useEffect. Tornou-se muito mais simples com o React Query.

Para entender melhor, vamos comparar como a busca de dados é implementada no useEffect e no React Query. Usaremos axios e a API JSONPlaceHolder para buscar as postagens.
// Fetch all posts
const fetchPosts = async () => {
  const { data } = await axios.get(
    "https://jsonplaceholder.typicode.com/posts"
  );
  return data;
};

Usando useEffect:
const [isLoading, setIsLoading] = useState(false);
const [data, setData] = useState([]);
const [error, setError] = useState(null);

useEffect(() => {
  setIsLoading(true);
  try {
    (async () => {
      const data = await fetchPosts();
      setData(data);
    })();
  } catch (error) {
    setError(error);
  } finally {
    setIsLoading(false);
  }
}, []);

Usando o React Query:
const { isLoading, data, error } = useQuery("posts", fetchPosts);

O React Query fornece um gancho useQuery para buscar dados do servidor. Toda a implementação do useEffect foi substituída por uma única linha no React Query. Ele apagou um monte de clichê e torna seu código fácil de ler. O carregamento, o erro e o estado dos dados são tratados fora da caixa. Além disso, ele também ajuda com cache, sincronização de fundo e um monte de outras coisas.

Conclusão

React Query é uma biblioteca fantástica. Na maioria dos casos, ele elimina a necessidade de seus gerentes de estado globais. Ele também ajuda a apagar um monte de clichê e facilita o uso em grandes aplicações. Além disso, ele lida com cache, atualizações em segundo plano, tentativas de solicitação, otimizações de desempenho e outras coisas.

Antes de começar a integrar essa biblioteca a todo vapor, você deve examinar cuidadosamente se isso é algo que seu aplicativo precisa. Se você quiser usá-lo por suas funcionalidades essenciais, você deve considerar outras alternativas leves.
___

Tenho certeza de que você leu meu post anterior sobre como começar com o react-query, onde discuto como usar o gancho 'useQuery' na busca dos dados.

Portanto, 'useQuery' é usado apenas para buscar os dados, mas 'useMutation' é para solicitação post/put para o back-end.

Digamos que eu tenha uma função para solicitação de postagem.

Em userApi.js
import axios from 'axios'


const api = axios.create({
    baseURL : "https://gorest.co.in/public/v2",
    headers : {'Authorization' : `Bearer ${apiToken}`}
}


export const createUser = (newUser) => api.post("/users",newUser).then(res => res.data);
Essa função createUser usa um objeto de usuário e ele será postado na api gorest. Para o token, você só precisa se inscrever no site gorest.

Vamos criar uma página de formulário que leva nome, e-mail, gênero e status, que são as propriedades na api gorest. Acabei de lidar com o formData usando useState aqui.

UserForm.js
import React, {  useState } from 'react'


function UserForm() {
   const [userData,setUserData ] = useState({
        name : '',
        email : '',
        gender : '',
        status : ''
    })

    const handleFormChange = (e) => {
        setUserData(
            {
                ...userData,
                [e.target.name] : e.target.value
            }
        );
    }

    const handleSubmit = (e) => {
        e.preventDefault();
        console.log(userData)

    }

  return (
    <div>
        <form onSubmit={handleSubmit}>
            <div className='field-container'>
            <label htmlFor='name'>Name : </label>
            <input type="text"  name='name' onChange={handleFormChange} value={userData.name}/>
            </div>

            <div className='field-container'>
            <label htmlFor='name'>Email : </label>
            <input type="email"  name='email' onChange={handleFormChange} value={userData.email}/>
            </div>



            <div className='fieldContainer'>
            <label htmlFor='gender'>Gender : </label>
            <input type="radio"  name='gender' onChange={handleFormChange} value='female'/> Female

            <input type="radio"  name='gender' onChange={handleFormChange} value='male'/> Male

            </div>

            <div className='field-container'>
            <label htmlFor='gender'>Status : </label>
            <input type="radio"  name='status' onChange={handleFormChange} value='active'/> Active

            <input type="radio"  name='status' onChange={handleFormChange} value='inactive'/> Inactive

            </div>
            <input type='submit' value={'Submit'} />

        </form>
    </div>
  )
}

export default UserForm
Ao enviar este formulário, acabei de consolar o objeto userData. Você pode ver a saída na captura de tela a seguir.

Image description

Então, para postar os dados, você precisa acionar a função corretamente. Mas como fazemos isso usando useMutation.Vamos ver.
import { useMutation } from 'react-query'


const {mutate} = useMutation(createUser);

Esta mutação é uma função que deve ser acionada sempre que você precisar e os dados/carga útil que você deseja enviar devem ser enviados como um parâmetro para essa função mutar.

então minha função handleSubmit ficaria como
const handleSubmit = (e) => {
    e.preventDefault();
    mutate(userData);

Como lidamos em useQuery você pode ter isLoading para postar os dados. Com todas as modificações, acabei de mostrar a resposta na minha interface do usuário. vamos ver o código.
import React, {  useState } from 'react'
import { useMutation } from 'react-query'
import {createUser} from './userApi'

function UserForm() {
    const {mutate, isLoading} = useMutation(createUser, {
        onSuccess : (data) => {
            console.log(data) //This is the response you get back
            setResponse(data)
        }
    })
    const [response,setResponse] = useState({})
    const [userData,setUserData ] = useState({
        name : '',
        email : '',
        gender : '',
        status : ''
    })

    const handleFormChange = (e) => {
        setUserData(
            {
                ...userData,
                [e.target.name] : e.target.value
            }
        );
    }

    const handleSubmit = (e) => {
        e.preventDefault();
        mutate(userData)

    }

  return (
    <div>
        <form onSubmit={handleSubmit}>
            <div className='field-container'>
            <label htmlFor='name'>Name : </label>
            <input type="text"  name='name' onChange={handleFormChange} value={userData.name}/>
            </div>

            <div className='field-container'>
            <label htmlFor='name'>Email : </label>
            <input type="email"  name='email' onChange={handleFormChange} value={userData.email}/>
            </div>



            <div className='fieldContainer'>
            <label htmlFor='gender'>Gender : </label>
            <input type="radio"  name='gender' onChange={handleFormChange} value='female'/> Female

            <input type="radio"  name='gender' onChange={handleFormChange} value='male'/> Male

            </div>

            <div className='field-container'>
            <label htmlFor='gender'>Status : </label>
            <input type="radio"  name='status' onChange={handleFormChange} value='active'/> Active

            <input type="radio"  name='status' onChange={handleFormChange} value='inactive'/> Inactive

            </div>
            <input type='submit' value={'Submit'} />

        </form>
        {isLoading && <div>Posting the data...</div>}
        {Object.keys(response).length > 0 && (<div>
            <h4>Response</h4>
            <p>{response.name}</p>
            <p>{response.email}</p>
        </div>)}
    </div>
  )
}

export default UserForm
O método onSuccess será acionado assim que sua solicitação de postagem for bem-sucedida e os dados forem a resposta retornada após a solicitação de postagem.

A saída que gerei na captura de tela a seguir.

Image description

Haveria algumas etapas adicionais que precisam ser cuidadas sempre que você atualizar alguns valores (fazer solicitação de colocação) e você deseja que a alteração seja refletida de volta. Mas eu quero que cada um dos meus posts seja o essencial, então vou deixar para o próximo tópico. Espero que isso seja útil