Explore o streaming de resposta com React Server Actions para formulários progressivos. Aprenda a criar formulários mais rápidos e responsivos para uma melhor experiência do usuário.
Streaming de Resposta com React Server Actions: Resposta Progressiva de Formulário para UX Aprimorada
As React Server Actions introduzem uma poderosa mudança de paradigma na forma como lidamos com operações do lado do servidor em nossas aplicações React. Uma das características mais empolgantes é a capacidade de transmitir respostas progressivamente, permitindo-nos fornecer feedback imediato aos usuários mesmo antes da operação inteira ser concluída. Isso é particularmente benéfico para formulários, onde podemos criar uma experiência de usuário mais responsiva e envolvente ao atualizar a UI à medida que os dados se tornam disponíveis.
Entendendo as React Server Actions
As Server Actions são funções assíncronas que rodam no servidor, iniciadas a partir de componentes React. Elas oferecem várias vantagens sobre as chamadas de API tradicionais:
- Segurança Aprimorada: As Server Actions rodam diretamente no servidor, reduzindo o risco de expor dados ou lógica sensível ao cliente.
- Redução de Código Repetitivo: Elas eliminam a necessidade de rotas de API separadas e lógica de busca de dados no cliente.
- Performance Melhorada: Elas podem aproveitar a renderização do lado do servidor (SSR) e o cache para tempos de carregamento iniciais mais rápidos e melhor performance.
- Segurança de Tipos (Type Safety): Com TypeScript, as Server Actions fornecem segurança de tipos de ponta a ponta, garantindo a consistência dos dados entre o cliente e o servidor.
O Poder do Streaming de Resposta
O envio de formulários tradicionais geralmente envolve o envio de todos os dados para o servidor, a espera por uma resposta e, em seguida, a atualização da UI. Isso pode levar a um atraso perceptível, especialmente para formulários complexos ou conexões de rede lentas. O streaming de resposta permite que o servidor envie dados de volta ao cliente em partes (chunks), permitindo-nos atualizar a UI progressivamente à medida que os dados se tornam disponíveis.
Imagine um formulário que calcula um preço complexo com base na entrada do usuário. Em vez de esperar que todo o cálculo seja concluído, o servidor pode transmitir resultados intermediários de volta ao cliente, fornecendo feedback em tempo real ao usuário. Isso pode melhorar significativamente a experiência do usuário e fazer com que a aplicação pareça mais responsiva.
Implementando Resposta Progressiva de Formulário com Server Actions
Vamos ver um exemplo de como implementar uma resposta progressiva de formulário com React Server Actions.
Exemplo: Um Conversor de Moedas em Tempo Real
Criaremos um formulário simples de conversor de moedas que fornece atualizações da taxa de câmbio em tempo real à medida que o usuário digita o valor.
1. Configurando a Server Action
Primeiro, definimos a Server Action que lida com a conversão de moeda.
// server/actions.ts
'use server';
import { unstable_cache } from 'next/cache';
async function getExchangeRate(fromCurrency: string, toCurrency: string): Promise<number> {
// Simula a busca da taxa de câmbio de uma API externa
console.log(`Fetching exchange rate for ${fromCurrency} to ${toCurrency}`);
await new Promise(resolve => setTimeout(resolve, 500)); // Simula o atraso da rede
if (fromCurrency === 'USD' && toCurrency === 'EUR') return 0.92;
if (fromCurrency === 'EUR' && toCurrency === 'USD') return 1.09;
if (fromCurrency === 'USD' && toCurrency === 'JPY') return 145;
if (fromCurrency === 'JPY' && toCurrency === 'USD') return 0.0069;
throw new Error(`Exchange rate not found for ${fromCurrency} to ${toCurrency}`);
}
export const convertCurrency = async (prevState: any, formData: FormData) => {
const fromCurrency = formData.get('fromCurrency') as string;
const toCurrency = formData.get('toCurrency') as string;
const amount = Number(formData.get('amount'));
try {
if (!fromCurrency || !toCurrency || isNaN(amount)) {
return { message: 'Please provide valid input.' };
}
// Simula o streaming da resposta
await new Promise(resolve => setTimeout(resolve, 250));
const exchangeRate = await unstable_cache(
async () => getExchangeRate(fromCurrency, toCurrency),
[`exchange-rate-${fromCurrency}-${toCurrency}`],
{ tags: [`exchange-rate-${fromCurrency}-${toCurrency}`] }
)();
await new Promise(resolve => setTimeout(resolve, 250));
const convertedAmount = amount * exchangeRate;
return { message: `Converted amount: ${convertedAmount.toFixed(2)} ${toCurrency}` };
} catch (e: any) {
console.error(e);
return { message: 'Failed to convert currency.' };
}
};
Neste exemplo, a Server Action convertCurrency
busca a taxa de câmbio (simulada com um atraso) e calcula o valor convertido. Adicionamos atrasos artificiais usando setTimeout
para simular a latência da rede e demonstrar o efeito de streaming.
2. Implementando o Componente React
Em seguida, criamos o componente React que usa a Server Action.
// app/page.tsx
'use client';
import { useState, useTransition } from 'react';
import { convertCurrency } from './server/actions';
import { useFormState } from 'react-dom';
export default function CurrencyConverter() {
const [fromCurrency, setFromCurrency] = useState('USD');
const [toCurrency, setToCurrency] = useState('EUR');
const [amount, setAmount] = useState('');
const [isPending, startTransition] = useTransition();
const [state, formAction] = useFormState(convertCurrency, { message: '' });
const handleSubmit = async (event: React.FormEvent) => {
event.preventDefault();
startTransition(() => {
formAction(new FormData(event.target as HTMLFormElement));
});
};
return (
<div>
<h2>Real-Time Currency Converter</h2>
<form action={handleSubmit}>
<label htmlFor="fromCurrency">From:</label>
<select id="fromCurrency" name="fromCurrency" value={fromCurrency} onChange={(e) => setFromCurrency(e.target.value)}>
<option value="USD">USD</option>
<option value="EUR">EUR</option>
<option value="JPY">JPY</option>
</select>
<label htmlFor="toCurrency">To:</label>
<select id="toCurrency" name="toCurrency" value={toCurrency} onChange={(e) => setToCurrency(e.target.value)}>
<option value="EUR">EUR</option>
<option value="USD">USD</option>
<option value="JPY">JPY</option>
</select>
<label htmlFor="amount">Amount:</label>
<input
type="number"
id="amount"
name="amount"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
<button type="submit" disabled={isPending}>
{isPending ? 'Converting...' : 'Convert'}
</button>
</form>
<p>{state.message}</p>
</div>
);
}
Pontos-chave:
- Usamos o hook
useFormState
para gerenciar o estado do formulário e invocar a Server Action. - O estado
isPending
deuseTransition
desabilita o botão de envio e mostra a mensagem "Convertendo..." enquanto a ação está em execução, dando feedback ao usuário. - A função
formAction
retornada poruseFormState
lida automaticamente com o envio do formulário e atualiza o estado com a resposta da Server Action.
3. Entendendo as Atualizações Progressivas
Quando o usuário envia o formulário, a função handleSubmit
é chamada. Ela cria um objeto FormData
a partir do formulário e o passa para a função formAction
. A Server Action então é executada no servidor. Devido aos atrasos artificiais introduzidos na Server Action, você observará o seguinte:
- O botão de envio muda para "Convertendo..." quase imediatamente.
- Após um pequeno atraso (250ms), o código simula a obtenção da taxa de câmbio.
- O valor convertido é calculado e o resultado é enviado de volta para o cliente.
- O
state.message
no componente React é atualizado, exibindo o valor convertido.
Isso demonstra como o streaming de resposta nos permite fornecer atualizações intermediárias ao usuário à medida que os dados se tornam disponíveis, levando a uma experiência de usuário mais responsiva e envolvente.
Benefícios da Resposta Progressiva de Formulário
- Melhor Experiência do Usuário: Fornece feedback imediato aos usuários, fazendo com que a aplicação pareça mais responsiva e menos lenta.
- Redução da Latência Percebida: Ao mostrar resultados intermediários, os usuários percebem o processo como mais rápido, mesmo que a operação geral leve o mesmo tempo.
- Maior Engajamento: Mantém os usuários engajados, fornecendo atualizações em tempo real e evitando que abandonem o formulário devido a atrasos percebidos.
- Aumento das Taxas de Conversão: Uma experiência de usuário mais suave e responsiva pode levar a taxas de conversão mais altas, especialmente para formulários complexos.
Técnicas Avançadas
1. Usando `useOptimistic` para Atualizações Imediatas da UI
O hook useOptimistic
permite que você atualize a UI de forma otimista antes que a Server Action seja concluída. Isso pode fornecer um tempo de resposta percebido ainda mais rápido, pois a UI reflete o resultado esperado imediatamente.
import { useOptimistic } from 'react';
function MyComponent() {
const [optimisticState, addOptimistic] = useOptimistic(
initialState,
(state, newUpdate) => {
// Retorna o novo estado com base na atualização
return { ...state, ...newUpdate };
}
);
const handleClick = async () => {
addOptimistic({ someValue: 'optimistic update' });
await myServerAction();
};
return (
<div>
<p>{optimisticState.someValue}</p>
<button onClick={handleClick}>Update</button>
</div>
);
}
No exemplo do conversor de moedas, você poderia atualizar otimisticamente o valor convertido com base na taxa de câmbio atual, fornecendo uma prévia imediata ao usuário antes que o cálculo real seja concluído no servidor. Se o servidor retornar um erro, você pode reverter a atualização otimista.
2. Implementando Tratamento de Erros e Mecanismos de Fallback
É crucial implementar um tratamento de erros robusto e mecanismos de fallback para lidar com casos em que a Server Action falha ou a conexão de rede é interrompida. Você pode usar o bloco try...catch
dentro da Server Action para capturar erros e retornar uma mensagem de erro apropriada para o cliente.
// server/actions.ts
export const convertCurrency = async (prevState: any, formData: FormData) => {
// ...
try {
// ...
} catch (error: any) {
console.error(error);
return { message: 'An error occurred while converting the currency. Please try again later.' };
}
};
Do lado do cliente, você pode exibir a mensagem de erro para o usuário e fornecer opções para tentar a operação novamente ou entrar em contato com o suporte.
3. Cache de Taxas de Câmbio para Performance
Buscar taxas de câmbio de uma API externa pode ser um gargalo de performance. Para melhorar o desempenho, você pode armazenar em cache as taxas de câmbio usando um mecanismo de cache como Redis ou Memcached. O unstable_cache
do Next.js (como usado no exemplo) fornece uma solução de cache integrada. Lembre-se de invalidar o cache periodicamente para garantir que as taxas de câmbio estejam atualizadas.
4. Considerações sobre Internacionalização
Ao construir aplicações para um público global, é importante considerar a internacionalização (i18n). Isso inclui:
- Formatação de Números: Usar formatos de número apropriados para diferentes localidades (por exemplo, usando vírgulas ou pontos como separadores decimais).
- Formatação de Moeda: Exibir símbolos e formatos de moeda de acordo com a localidade do usuário.
- Formatação de Data e Hora: Usar formatos de data e hora apropriados para diferentes localidades.
- Localização: Traduzir a UI para diferentes idiomas.
Bibliotecas como Intl
e react-intl
podem ajudá-lo a implementar i18n em suas aplicações React.
Exemplos do Mundo Real e Casos de Uso
- E-commerce: Exibição de custos de envio e estimativas de entrega em tempo real à medida que o usuário adiciona itens ao carrinho.
- Aplicações Financeiras: Fornecimento de cotações de ações e atualizações de portfólio em tempo real.
- Reservas de Viagem: Mostrando preços e disponibilidade de voos em tempo real.
- Visualização de Dados: Streaming de atualizações de dados para tabelas e gráficos.
- Ferramentas de Colaboração: Exibição de atualizações em tempo real para documentos e projetos.
Conclusão
O streaming de resposta com React Server Actions oferece uma maneira poderosa de aprimorar a experiência do usuário de suas aplicações React. Ao fornecer respostas progressivas em formulários, você pode criar formulários mais rápidos, responsivos e envolventes que mantêm os usuários engajados e melhoram as taxas de conversão. Combinando o streaming de resposta com técnicas como atualizações otimistas e cache, você pode construir experiências de usuário verdadeiramente excepcionais.
À medida que as React Server Actions continuam a evoluir, podemos esperar o surgimento de recursos e capacidades ainda mais poderosos, simplificando ainda mais o desenvolvimento de aplicações web complexas e dinâmicas.
Para Explorar Mais
Este guia fornece uma visão abrangente do streaming de resposta com React Server Actions e sua aplicação em respostas progressivas de formulários. Ao entender os conceitos e as técnicas discutidas aqui, você pode aproveitar esse poderoso recurso para construir aplicações web mais rápidas, responsivas e envolventes.