O React nos oferece flexibilidade em como escolhemos resolver os problemas (como gerenciamento de estado, rede e estilo) em nossos aplicativos. Uma ótima base de código tem pontos de problema identificados e tratados com um padrão reproduzível que é padrão e consistente.
E, como engenheiros de front-end, é crucial transmitir informações sobre mudanças no estado da rede para o usuário, como a maioria dos aplicativos construímos a necessidade de interagir com um ou mais servidores. Podemos atingir essas metas usando React Hooks personalizados.
Em Neste artigo, abordarei os vários estados em que as solicitações de rede existem e mostrarei como manter o gerenciamento de solicitações nos Ganchos personalizados. Também vou orientá-lo na construção de um pequeno aplicativo que emprega esses ganchos.
O que é uma solicitação de rede?
Uma solicitação de rede normalmente existe nestes estados:
inativo erro de carregamento/processamento/sucesso em andamento
O estado de solicitação de rede ociosa é a fase padrão (e final) para uma solicitação de rede. Durante a fase de carregamento, o cliente aguarda a confirmação e pacotes do servidor e, em seguida, passa para o estado de sucesso ou erro.
Transições de estado da rede.
Localizando solicitações de rede usando Ganchos React personalizados
Para manter as solicitações de rede testáveis e desacopladas da lógica de negócios, é melhor gerenciar solicitações com Ganchos personalizados. Isso mantém seu código enxuto e facilita a execução de operações especiais únicas, como transformações de dados em respostas de rede.
Por exemplo, uma solicitação para buscar uma lista de postagens de blog pode ser mantida em um gancho personalizado usePostsQuery , assim como o abaixo:
import {useState, useEffect} de’react’const api={GET: async (url)=> {const response=await fetch (url); dados const=aguarda resposta.json (); dados de retorno; }} função padrão de exportação usePostsQuery () {const [erro, setError]=useState () const [status, setStatus]=useState (‘idle’) const [data, setData]=useState () const startFetch=async ()=> {try {let data=await api.GET (‘/posts’) setError () setStatus (‘sucesso’) setData (dados)} catch (erro) {setError (erro) setStatus (‘error’)}} useEffect (( )=> {startFetch ()}, []); return {data, error, isSuccess: status===’success’, isError: status===’error’refetch: startFetch}}
Este Gancho pode ser ainda mais conciso aproveitando React Query (minha ferramenta preferida):
importação {useQuery} de”react-query”; função padrão de exportação usePostsQuery () {return useQuery (“posts”, ()=> api.GET (“/posts”)); }
Criando o projeto
Vamos construir um pequeno aplicativo chamado Betflix, que vocês dois podem visitar e clone . Este aplicativo permitirá que amigos escolham times esportivos em um conjunto de jogos e façam previsões.
Observação : por uma questão de brevidade, deixarei de explicar mais componentes mundanos usados nesta prova de conceito. Você está convidado a explorar o código inteiro para isso .
Em primeiro lugar, criaremos um novo projeto React e iniciaremos o servidor de desenvolvimento:
npx create-react-app betflix cd betflix npm start
Precisamos instalar dependências para solicitações HTTP, uma função sem servidor proxy e um banco de dados gerenciado (para manter acessórios e outros registros).
npm install react-query react-toast-notificações http-proxy-middleware @ supabase/supabase-js–save
Eu também inclua Netlify Lambda e CLI como dependências de desenvolvimento.
npm install netlify-lambda netlify-D
Quando terminarmos, você deverá ter uma estrutura de diretório como a abaixo.
Exibindo a lista de acessórios com React Hooks personalizados
Atualizaremos o componente
import”./App.css”; importar Fixture de”./components/Fixture”; importar o carregador de”./components/Loader”; import useBetsQuery de”./hooks/queries/useBetsQuery”; import useFixturesQuery de”./hooks/queries/useFixturesQuery”; função App () {const {data, isLoading, isError}=useFixturesQuery (); const {dados: apostas, isLoading: betsLoading, isError: betsErrored,}=useBetsQuery (); if (isLoading || betsLoading) return
Encontramos um erro ao buscar dados
; const sortFixtures=(fixtureA, fixtureB)=> {return (bets.hasOwnProperty (fixtureB.fixture.id)-bets.hasOwnProperty (fixtureA.fixture.id) || fixtureB.fixture.status.elapsed-fixtureA.fixture.status. decorrido); }; return (
)}
); } exportar aplicativo padrão;
A partir do código acima, declaramos dependências em useBetsQuery e useFixturesQuery, então agora vamos defini-las.
useBetsQuery é um Gancho personalizado usado para buscar e transformar uma lista de apostas em um mapa de objetos com chave que podemos usar para rastrear o status da aposta de um dispositivo elétrico.
Vamos criar useBetsQuery.js em/src/hooks/queries e atualizá-lo:
import {useQuery} from”react-consulta”; const ENDPOINT=”/.netlify/functions/fetchBets”;//Normaliza a carga útil das apostas em um mapa codificado com `fixture_id` como a função-chave normalizeBets (betsList) {return betsList.reduce ((acc, curr)=> ({… acc, [curr.fixture_id]: curr, }), {}); }//Como usaremos a API fetch (em vez de Axios), precisamos retornar explicitamente uma//Promessa quando ocorrer um erro para que o React Query possa alterar o status. const getBets=async (url)=> {const response=await fetch (url); dados const=aguarda resposta.json (); if (response.ok) {return normalizeBets (data.results); } return Promise.reject (new Error (data.message)); }; função padrão de exportação useBetsQuery () {return useQuery (“bets”, ()=> getBets (ENDPOINT)); }
Feito isso, também precisamos criar o Gancho personalizado onde buscaremos. Crie o gancho useFixturesQuery.js em src/hooks/queries e adicione o código abaixo:
import {useQuery} from”react-query”; const getFixtures=async (url)=> {const response=await fetch (url); dados const=aguarda resposta.json (); dados de retorno; }; função padrão de exportação useFixturesQuery () {return useQuery (“fixtures”, ()=> getFixtures (“/. netlify/functions/fetchFixtures”)); }
Agora estamos prontos para definir o componente que exibirá informações sobre o acessório individual.
Criando o componente
Vamos criar o
O useMutationNotification é um gancho personalizado interessante que nos permite lidar com as mudanças de estado da rede de uma maneira ergonômica previsível para que possamos fornecer feedback sobre as ações iniciadas pelo usuário imediatamente.
import {useEffect, useState} de”react”; import {useToasts} de”react-toast-notificações”; import {ReactComponent as ArrowLeft} de”../assets/svg/arrowLeft.svg”; import {ReactComponent as ChevronRight} de”../assets/svg/chevronRight.svg”; importar TeamCard de”./TeamCard”; importar FormInput de”./FormInput”; import useMutationNotification de”../hooks/useMutationNotification”; import usePlaceBetMutation de”../hooks/queries/usePlaceBetMutation”; importar o carregador de”./Loader”; function Fixture ({fixture, away, home, isBetPlaced, defaultAmount, defaultSelectedTeam,}) {const [amount, setAmount]=useState (defaultAmount || 0); const [selectedTeam, setSelectedTeam]=useState (defaultSelectedTeam); const [betPlaced, setBetPlaced]=useState (isBetPlaced); const {addToast}=useToasts (); const [doPlaceBetRequest, placeBetState]=usePlaceBetMutation (); useMutationNotification ({… placeBetState, useServerMessage: false, entity:”aposta”, actionType:”place”,}); useEffect (()=> {if (placeBetState.isSuccess) setBetPlaced (true);}, [placeBetState.isSuccess]); times const={fora, em casa,}; const status=! fixture.status.elapsed?”Próximo”:”Em andamento”; const doAmountUpdate=(e)=> setAmount (e.target.value); const doTeamUpdate=(equipe)=> {if (betPlaced) return; setSelectedTeam (equipe); }; const doPlaceBet=()=> {if (! selectedTeam || amount =0) {addToast (“Selecione uma equipe e adicione uma quantia”, {aparência:”info”, autoDismiss: true,}); Retorna; } doPlaceBetRequest ({quantidade, escolha: selectedTeam, fixture_id: fixture.id,}); }; return (
{! betPlaced? (>
{! placeBetState.isLoading? (): (
): (
Você fez uma aposta de $ {amount} em {“”} {times [selectedTeam] ?. name} para potencialmente ganhar $ {amount * 2}
)}
); } Fixture.defaultProps={isBetPlaced: false,}; exportar Fixture padrão;
No código acima, declaramos dependências de alguns ganchos.
useMutationNotification aceitará as opções de status de solicitação de rede (isError e isSuccess) e nos permitirá mostrar a mensagem de erro do servidor ( se definirmos useServerMessage como true) ou passar strings de entidade e actionType para fornecer uma mensagem genérica ao usuário.
Vamos criar useMutationNotification.js em src/hooks e atualizá-lo com o código abaixo:
import {useEffect, useState} de”react”; import useShowToast de”./useShowToast”; função capFirst (string) {return string.charAt (0).toUpperCase () + string.slice (1); } function useMutationNotification ({isError, isSuccess, actionType=”criar”, entidade, dados, erro, useServerMessage=true,}) {const [notificationConfig, setNotificationConfig]=useState (null); const showToast=useShowToast (); useEffect (()=> {if (isError) {setNotificationConfig ({type:”error”, message: useServerMessage? error.message: `$ {entity} não pode ser $ {actionType} d`,});}}, [useServerMessage, isError, setNotificationConfig, entity, actionType, error,]); useEffect (()=> {if (isSuccess) {setNotificationConfig ({type:”success”, message: useServerMessage? data.message: `$ {entity} com sucesso $ {actionType} d`,});}}, [useServerMessage , isSuccess, setNotificationConfig, entity, actionType, data,]); useEffect (()=> {if (notificationConfig) {const {type, message}=notificationConfig; showToast ({type, message: capFirst (message)});}}, [notificationConfig, showToast]); } export default useMutationNotification;
Em seguida, definiremos a mutação usePlaceBet que pretendemos usar ao fazer a aposta. Retornaremos a ação de mutação e seu estado. Crie usePlaceBetMutation em src/hooks/queries e atualize-o para o seguinte código:
import {useMutation} from”react-query”; const ENDPOINT=”/.netlify/functions/placeBet”; função padrão de exportação usePlaceBetMutation () {solicitação const=async (carga útil)=> {res const=aguarda busca (ENDPOINT, {método:”POST”, corpo: JSON.stringify (carga útil),}); dados const=aguarda res.json (); if (! res.ok) return Promise.reject (new Error (data.message)); dados de retorno; }; const {mutate,… mutationState}=useMutation (solicitação); return [mutate, mutationState]; }
Com essas atualizações feitas, agora podemos lidar com as alterações de estado da rede para mutações de maneira simples e fácil de ler.
Conclusão
Reagindo às mudanças de estado da rede pode ser desafiador, mas também é uma grande oportunidade de fornecer aos usuários uma experiência muito mais significativa.
Você pode verificar o documentação React Query para aprender mais sobre como aprimorar a experiência do estado da rede para seus usuários ao construir aplicativos React. Você pode encontrar o código-fonte completo para esta prova de conceito de demonstração em GitHub .