Gerenciando dados de rascunho em JavaScript

Um usuário preenche um formulário, solicita uma visualização e depois muda de ideia. Eles clicam em “Limpar”, as entradas ficam vazias e a interface parece pronta para outra pessoa.
Um momento depois, a prévia anterior retorna.
A aplicação limpou os campos, mas uma solicitação assíncrona ainda mantinha os valores antigos e atualizava a tela quando finalizada. Problemas semelhantes ocorrem quando os rascunhos permanecem no armazenamento do navegador, as mensagens de validação retêm detalhes pessoais ou o estado interno de um componente sobrevive a uma redefinição visual.
Para desenvolvedores que criam fluxos de trabalho de integração, despesas, aplicativos e documentos, limpar um formulário é uma operação de gerenciamento de estado. Merece a mesma atenção que a submissão.
Este artigo explica como definir essa operação, evitar resultados obsoletos e inspecionar o resultado usando navegador prático e ferramentas de teste.
Defina o que o usuário está limpando
Comece listando os locais onde existe um rascunho. As entradas visíveis são apenas um local.
Localização Exemplo O que a compensação precisa abordar Controles de formulário Nome, valor, notas Restaure o estado vazio pretendido Estado do aplicativo Estado React ou um objeto JavaScript Remover valores de rascunho e dados derivados Saída renderizada Visualização, resumo, mensagem de erro Remover conteúdo gerado do rascunho Trabalho pendente Solicitação de busca ou validação atrasada Cancele sempre que possível e rejeite resultados obsoletos Armazenamento do navegador Uma entrada de salvamento automático Remova a entrada específica do rascunho, se presente Armazenamento do servidor Um rascunho salvo Use uma operação de exclusão autenticada se a exclusão for prometidaAo revisar uma interface de dados financeiros como esseum exercício de design útil é mapear quais informações pertencem ao rascunho atual e o que deve acontecer quando o usuário começar novamente. O mesmo exercício se aplica ao envio de despesas e à integração do cliente; a implementação deve ser avaliada separadamente da interface visível.
Nomeie as ações de acordo com seu escopo real. “Limpar este formulário” não deve significar que os registros enviados anteriormente foram excluídos. Se um rascunho tiver sido salvo remotamente, diferencie a limpeza da tela atual da exclusão do rascunho salvo.
Para uma introdução mais ampla à estrutura e validação de formulários, o SitePoint guia para formulários HTML modernos fornece informações úteis.
Escolha uma política de armazenamento antes de adicionar o salvamento automático
O salvamento automático é conveniente, mas persistir o formulário inteiro após cada pressionamento de tecla é uma decisão arquitetônica, não um aprimoramento inofensivo da interface do usuário.
Para um fluxo de trabalho curto, manter o rascunho na memória pode ser suficiente. Para um fluxo de trabalho mais longo, decida quais campos podem ser retidos, como o usuário entende esse comportamento e quando a cópia retida será removida.
As opções de armazenamento do navegador têm durações diferentes. De acordo com Documentação sessionStorage do MDNo armazenamento de sessão é associado a uma sessão de página e sobrevive a recargas. Não deve ser tratado como equivalente a uma variável comum que desaparece quando a página é recarregada.
A retenção mais curta também não torna o armazenamento um limite de segurança. O Orientação de segurança OWASP HTML5 adverte contra o armazenamento de informações confidenciais no armazenamento local porque o JavaScript em execução na origem pode acessá-las.
Uma política inicial útil é:
- Mantenha os valores de rascunho transitórios na memória, a menos que seja necessária persistência.
- Armazene apenas os campos necessários para o recurso de recuperação.
- Dê a cada rascunho persistente uma chave ou identificador específico.
- Defina a limpeza para conclusão, cancelamento e alterações de conta.
- Evite colocar valores de rascunho em URLs ou logs de diagnóstico.
Isso reduz cópias desnecessárias. Ele não fornece apagamento seguro de memória nem protege uma página de todos os scripts que podem acessar seus dados.
Entenda os limites do form.reset()
O nativo form.reset() O método restaura os controles para seus valores padrão. Isso não torna necessariamente todos os campos vazios.
Por exemplo, uma entrada renderizada com um valor padrão pode retornar a esse valor após uma redefinição. O método também não limpa um elemento de visualização separado, invalida uma resposta de rede ou atualiza todas as variáveis de estado gerenciadas pela estrutura.
Use um pequeno formulário HTML com padrões vazios para o exemplo a seguir:
<form id="draft-form"> <label for="full-name">Full name</label> <input id="full-name" name="fullName" required> <label for="notes">Notes</label> <textarea id="notes" name="notes"></textarea> <button type="submit">Preview</button> <button type="button" id="clear-draft">Clear form</button> </form> <p id="status" role="status"></p> <pre id="preview"></pre>O controle claro usa type="button" para que o aplicativo possa coordenar a operação completa.
Em uma implementação React ou Vue, aplique a mesma política ao estado que controla as entradas. Limpar o DOM e deixar o estado do componente intacto pode fazer com que os valores antigos reapareçam.
Impedir que solicitações antigas restaurem conteúdo limpo
O cancelamento ajuda a reduzir o trabalho desnecessário, mas a identidade da solicitação é o que protege a interface de um resultado obsoleto.
Atribua a cada solicitação um número de geração. Sempre que o saque for alterado ou liberado, avance esse número. Uma resposta poderá atualizar a UI somente se seu número ainda corresponder à geração atual.
O JavaScript a seguir funciona com o HTML acima. Assume uma mesma origem POST /api/preview endpoint que aceita JSON e retorna {"summary":"plain text"}. Esse endpoint é um requisito de integração; não é implementado aqui.
const form = document.querySelector("#draft-form"); const status = document.querySelector("#status"); const preview = document.querySelector("#preview"); const clearButton = document.querySelector("#clear-draft"); let generation = 0; let activeController = null; function invalidatePreview() { generation += 1; activeController?.abort(); activeController = null; preview.textContent = ""; status.textContent = ""; } form.addEventListener("input", invalidatePreview); form.addEventListener("submit", async (event) => { event.preventDefault(); if (!form.reportValidity()) return; invalidatePreview(); const requestGeneration = generation; const controller = new AbortController(); activeController = controller; const fields = new FormData(form); const payload = { fullName: String(fields.get("fullName") ?? ""), notes: String(fields.get("notes") ?? ""), }; status.textContent = "Preparing preview…"; try { const response = await fetch("/api/preview", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), signal: controller.signal, }); if (!response.ok) { throw new Error("Preview request failed"); } const result = await response.json(); if ( requestGeneration !== generation || controller.signal.aborted ) return; if (typeof result.summary !== "string") { throw new Error("Unexpected preview response"); } preview.textContent = result.summary; status.textContent = "Preview ready."; } catch (error) { if ( requestGeneration !== generation || controller.signal.aborted ) return; status.textContent = "Preview unavailable. Please try again."; } finally { if (activeController === controller) { activeController = null; } } }); clearButton.addEventListener("click", () => { invalidatePreview(); form.reset(); status.textContent = "Form cleared."; document.querySelector("#full-name").focus(); });A edição de um campo também invalida a visualização. Caso contrário, os usuários poderão confundir um resultado baseado em valores anteriores com um resultado baseado em sua entrada atual.
O código escreve texto fornecido pelo servidor com textContent em vez de interpretá-lo como HTML. Ele também exibe uma mensagem de falha corrigida em vez de repetir os valores da solicitação em um erro.
AbortarControlador permite que o cliente aborte o trabalho relacionado à busca. Abortar uma solicitação do navegador não garante que o processamento do servidor seja interrompido ou que os dados já recebidos pelo servidor sejam excluídos.
A verificação de geração reforça a promessa mais restrita: trabalho obsoleto não deve atualizar esta interface. SitePoints introdução à API Fetch explica a mecânica de solicitação e resposta por trás do exemplo.
Estenda a compensação aos dados que seu aplicativo realmente retém
O exemplo intencionalmente não salva rascunhos. Se o seu aplicativo fizer isso, adicione limpeza à operação de limpeza em vez de assumir que a redefinição do formulário resolve isso.
Remova a entrada de rascunho específica do aplicativo. Evitar localStorage.clear() como um atalho de limpeza geral, pois também remove chaves não relacionadas a essa origem.
Mantenha a interface do usuário honesta se a limpeza do armazenamento falhar. Não anuncie que todas as cópias salvas desapareceram quando o aplicativo esvaziou apenas os controles visíveis.
Inspecione também o estado menos óbvio:
- Resumos de validação que contêm valores enviados.
- Visualize caches mantidos por bibliotecas de busca de dados.
- Arquivos referenciados pelo estado do aplicativo.
- Retornos de chamada rejeitados aguardando para salvar um rascunho.
- Links de download apoiados por URLs de objetos.
- Outra guia que pode restaurar ou salvar novamente um rascunho antigo.
Um URL de objeto deve ser revogado quando não for mais necessário, mas revogá-lo não exclui um arquivo que o usuário já baixou.
Para alterações programáticas, chame a mesma função de invalidação usada pelos eventos de entrada. Atribuir um valor de entrada em JavaScript não aciona automaticamente o manipulador de eventos de entrada do usuário.
Você não precisa de uma grande pilha de dependências para verificar uma operação clara. Cada uma dessas ferramentas responde a uma pergunta diferente.
Ferramenta O que inspecionar Painel do aplicativo Chrome DevTools Se as chaves de rascunho permanecem no armazenamento do navegador Painel de rede do Chrome DevTools O que foi transmitido e como as solicitações se comportam durante a compensação Dramaturgo Se uma resposta atrasada pode preencher novamente a saída limpa Trabalhador de serviço simulado Como a interface se comporta com respostas lentas ou com falha controladas núcleo de machado com Playwright Problemas de acessibilidade detectáveis automaticamente nos estados relevantesdo Chrome guia de inspeção de armazenamento local explica como inspecionar entradas armazenadas. Verifique também o armazenamento da sessão e o IndexedDB se o aplicativo os utiliza; uma tabela de armazenamento local vazia não é prova de que não existe persistência do navegador.
Trabalhador de serviço simulado é útil para criar condições de resposta reproduzíveis durante o desenvolvimento. Use acessórios fictícios para que as sessões de depuração não dependam de registros pessoais reais.
Com Interceptação de rede de dramaturgomantenha uma resposta de visualização até que o teste a libere explicitamente. Preencha o formulário, solicite uma prévia, limpe o rascunho e só então libere a resposta. Afirme que a entrada permanece vazia e a visualização nunca retorna.
Uma liberação controlada é mais confiável do que esperar que um atraso fixo crie a corrida certa. Teste também duas solicitações concluídas na ordem inversa.
Torne o comportamento de limpeza parte dos critérios de aceitação
Uma revisão útil verifica a operação em vários pontos:
Cenário Resultado esperado Limpe antes de solicitar uma visualização As entradas retornam aos padrões vazios Limpar enquanto uma solicitação está pendente A saída tardia não pode preencher novamente a página Edite depois de solicitar uma visualização O resultado antigo é descartado Limpar após falha na validação Mensagens de erro específicas de rascunho desaparecem Recarregar depois de limpar um rascunho persistente O aplicativo não restaura esse rascunho Limpar usando o teclado A ação funciona e o foco se move de forma previsívelComplemente as verificações automatizadas de acessibilidade com testes de teclado e leitor de tela. O Guia de acessibilidade do dramaturgo explica que as verificações automatizadas detectam apenas alguns problemas de acessibilidade.
Verifique o comportamento do back-end separadamente. A validação, autorização, retenção e exclusão do servidor exigem implementação e testes próprios. Uma interface de navegador limpa não pode demonstrar essas garantias.
Trate a compensação como uma mudança de estado coordenada
Uma operação clara e confiável tem um escopo definido. Ele invalida o trabalho pendente, redefine o estado de rascunho do aplicativo, remove a saída derivada e trata a persistência de acordo com o comportamento declarado do produto.
A implementação pode permanecer pequena. O que importa é que a equipe saiba onde residem os dados preliminares e teste o que acontece quando os eventos chegam em uma ordem inconveniente.
Quando os desenvolvedores explicitam esses limites, “Limpar formulário” se torna uma ação previsível, em vez de uma redefinição visual que deixa o fluxo de trabalho anterior em execução.
Compartilhe esta publicação
Ajude a levar esta informação adiante — publique no seu perfil.