Quando o usuário curte um post ou envia uma mensagem, esperar a resposta do servidor para só então atualizar a tela deixa a interface com uma sensação de atraso. O hook useOptimistic, estabilizado no React 19, resolve exatamente isso: ele mostra um estado "otimista" — o resultado que você espera que a Action produza — enquanto a operação assíncrona ainda está em andamento, e volta automaticamente para o estado real quando ela termina.
O problema sem useOptimistic
Sem o hook, atualizar a UI antes da resposta do servidor exige controlar manualmente um estado "visual" separado do estado real, revertendo-o à mão em caso de erro:
function BotaoCurtir({ post }) {
const [curtidasExibidas, setCurtidasExibidas] = useState(post.curtidas);
async function curtir() {
setCurtidasExibidas(c => c + 1);
try {
await enviarCurtida(post.id);
} catch (e) {
setCurtidasExibidas(c => c - 1); // reverte manualmente
}
}
return <button onClick={curtir}>♥ {curtidasExibidas}</button>;
}
Como funciona o useOptimistic
O hook recebe o estado real atual e uma função de atualização, e devolve o estado otimista mais uma função addOptimistic para disparar uma atualização temporária. Enquanto nenhuma Action estiver pendente, o estado otimista é idêntico ao estado real; durante uma Action pendente, ele reflete o valor otimista até o estado real ser atualizado:
const [curtidasOtimistas, adicionarCurtidaOtimista] = useOptimistic(
post.curtidas,
(curtidasAtuais, incremento) => curtidasAtuais + incremento
);
Aplicando isso ao botão de curtir, a atualização visual acontece no mesmo instante do clique, dentro de uma Action — e se a chamada ao servidor falhar, o React descarta o valor otimista e volta para post.curtidas sozinho, sem precisar de um reverso manual:
function BotaoCurtir({ post }) {
const [curtidasOtimistas, adicionarCurtidaOtimista] = useOptimistic(
post.curtidas,
(curtidasAtuais, incremento) => curtidasAtuais + incremento
);
async function curtir() {
adicionarCurtidaOtimista(1);
await enviarCurtida(post.id); // atualiza post.curtidas no servidor
}
return (
<form action={curtir}>
<button>♥ {curtidasOtimistas}</button>
</form>
);
}
Exemplo prático: chat com envio otimista
Combinado com useActionState, é possível exibir a mensagem na lista antes mesmo do servidor confirmar o envio, marcando-a como pendente enquanto a Action não termina:
function Chat({ mensagens, enviarMensagem }) {
const [mensagensOtimistas, adicionarMensagemOtimista] = useOptimistic(
mensagens,
(atuais, novaMensagem) => [...atuais, { ...novaMensagem, pendente: true }]
);
async function handleSubmit(formData) {
const texto = formData.get('texto');
adicionarMensagemOtimista({ id: Date.now(), texto });
await enviarMensagem(texto);
}
return (
<>
<ul>
{mensagensOtimistas.map(m => (
<li key={m.id} style={{ opacity: m.pendente ? 0.5 : 1 }}>
{m.texto}
</li>
))}
</ul>
<form action={handleSubmit}>
<input name="texto" />
<button>Enviar</button>
</form>
</>
);
}
Quando a lista real mensagens chegar atualizada do servidor (via revalidação ou nova prop), o React substitui a versão otimista pela real automaticamente, removendo a marca de pendente.
Quando usar
- Curtidas, votos e contadores onde o resultado esperado é previsível e o feedback imediato importa mais do que a confirmação exata do servidor.
- Listas que recebem um novo item (mensagens, comentários, itens de carrinho) antes da confirmação da escrita.
- Qualquer Action onde reverter manualmente o estado visual em caso de erro adicionaria complexidade desnecessária.
Cuidados
useOptimistic não substitui o tratamento de erro: se a Action falhar, o estado otimista desaparece, mas cabe ao código da Action decidir se mostra uma mensagem de erro ou tenta de novo. O estado otimista também só existe enquanto a atualização do estado real correspondente não chega — chamar addOptimistic fora de uma Action (por exemplo, em um useEffect qualquer) funciona, mas perde o sentido do hook, que foi desenhado para o ciclo de vida de Actions.
Compatibilidade
useOptimistic foi introduzido e estabilizado no React 19 (dezembro de 2024), exportado diretamente do pacote react. Funciona em React DOM com Actions de formulário ou disparadas manualmente, sem flag ou configuração extra a partir dessa versão.
Conclusão
useOptimistic cobre um padrão de UI comum — mostrar o resultado esperado antes da confirmação do servidor — sem exigir um segundo estado controlado manualmente. O resultado é uma interface que responde no instante da interação e volta ao estado real sozinha, inclusive em caso de erro.
0 Comentário
Deixe seu Comentário!
Dúvida, sugestão ou só um "valeu!" — tudo é bem-vindo por aqui.