Estado do servidor guardado em useState: o erro mais caro
Buscar dados num useEffect e guardá-los num useState é o padrão mais ensinado do React, e o mais caro. O problema não é funcionar mal — é que, ao escrevê-lo, assumiu-se a manutenção de uma cache. E uma cache tem quatro obrigações que ninguém pediu.
1. As quatro obrigações
Saber quando os dados estão velhos.
Não repetir um pedido que já vai a caminho.
Revalidar quando faz sentido — ao voltar ao separador, depois de uma escrita.
Sobreviver a desmontar e montar o componente outra vez.
Não cumprir estas quatro coisas tem nomes conhecidos: o pedido feito duas vezes; a corrida em que uma resposta antiga chega depois e sobrepõe a nova; o ecrã desactualizado depois de voltar para trás; e dois componentes a mostrar números diferentes para a mesma coisa, ambos convencidos de que estão certos.
2. A pergunta que decide
Antes de escolher a ferramenta: estes dados são deste ecrã, ou são do servidor?
A posição do scroll, um menu aberto, um rascunho ainda não enviado, o separador seleccionado — é estado do ecrã. Nasce aqui, morre aqui, e o useState é exactamente a ferramenta certa.
A lista de artigos, o número de votos, o perfil de quem está autenticado — é do servidor. Copiá-lo para dentro de um useState cria uma segunda verdade sobre a mesma coisa. E duas verdades divergem sempre, em silêncio, como no artigo do Context a propósito do tema.
3. O que fazer, por ordem
Primeiro: renderizar no servidor e passar como propriedade. Metade destes casos desaparece aqui, e é o assunto do artigo dos Server Components.
Segundo: se tiver mesmo de ser no cliente, usar uma biblioteca cujo trabalho é ser essa cache. Elas existem porque as quatro obrigações são difíceis, não porque sejam modernas.
Terceiro: nunca escrever a cache à mão. É resolvido, e malfeito custa semanas a descobrir.
4. Como está feito aqui
Não há, neste site, um único useState a guardar dados vindos do servidor. O caso mais tentador seria a votação — contagens que mudam quando se carrega num botão. O componente recebe-as como propriedades e o que guarda em estado é apenas a mensagem da resposta:
// As contagens vêm do servidor. Depois de votar, a ação invalida a
// etiqueta e o Next volta a renderizar a árvore com os números já
// certos — não há contas feitas aqui.
const [estado, setEstado] = useState<EstadoFormulario>(null);A última frase do comentário é o artigo todo: não há contas feitas aqui. O cliente não escreve gostos + 1, porque isso seria adivinhar — e a adivinha pode discordar do servidor, que é quem sabe se o voto contou, se já existia, ou se foi retirado.
Em vez disso a acção invalida a etiqueta de cache, o servidor volta a renderizar, e os números chegam já certos. Uma verdade só, e a mesma para todos.
5. A excepção legítima
Há um caso em que mostrar um valor adivinhado é a decisão certa: quando se quer esconder a latência. Carregar num «gostei» e ver o número subir de imediato é melhor experiência do que esperar duzentos milissegundos.
A diferença em relação ao erro é que a adivinha tem fim definido. O useOptimistic do React 19 existe para isto: mostra-se o valor provisório, e quando a verdade chega ele é substituído, sem código de reconciliação. Está declarado como temporário, em vez de ficar guardado como se fosse verdade.
O resumo
Propriedades para o que é do servidor. useState para o que é do ecrã. useOptimistic quando se mente de propósito, por duzentos milissegundos. E, se a vontade for escrever um useEffect que busca dados, vale a pena parar na pergunta anterior: porque é que esta página não pode trazer isto já pronta?