Formulários¶
Boa parte do CREED é formulário: cadastro, login, o questionário que a pessoa responde. Formulário feito à mão diverge rápido — cada um valida de um jeito e mostra erro de outro. Esta página fecha a forma. ✅
A pilha, que já está instalada¶
| Peça | Papel |
|---|---|
react-hook-form |
estado dos campos, submit, foco no erro |
zod |
o schema: o que é válido |
@hookform/resolvers/zod |
liga os dois (zodResolver) |
src/components/ui/field.tsx, input.tsx, label.tsx |
os campos |
react-i18next |
rótulo, placeholder e mensagem de erro |
Formulário não é motivo para trazer biblioteca nova.
As regras¶
- Todo formulário nasce com schema zod. Nada de
useStatepor campo nem deif (!valor)espalhado no submit. - O schema mora em
src/lib/validadores.ts, umexportpor schema (loginSchema,cadastroSchema). - O tipo sai do schema, não é escrito duas vezes:
export type CadastroForm = z.infer<typeof cadastroSchema>. - O schema espelha o contrato do back: obrigatório no Pydantic é obrigatório no
zod;
max_length=200vira.max(200). Divergiu, o back ganha. - A mensagem de erro é uma chave de i18n, não texto:
z.string().min(1, 'comum:erros.obrigatorio'). A tela traduz. - O estado do formulário não vai para o Redux. Ele vive no
react-hook-formaté o submit; o que vai para o slice é o resultado da chamada. - Erro do back cai no campo certo. 409 de e-mail repetido vira erro no campo
e-mail (
setError('email', ...)), não um alerta solto no topo. Erro que não é de campo (5xx) vira mensagem do formulário. - Botão de envio desabilitado enquanto envia, com o estado vindo de
formState.isSubmitting— não de umuseStateparalelo.
Acessibilidade — que aqui é lint 🔒¶
O eslint-plugin-jsx-a11y roda no npm run check. Na prática:
- todo input tem
<Label htmlFor>ligado aoid; - a mensagem de erro é associada ao campo (
aria-describedby) e anunciada (role="alert"); - campo inválido carrega
aria-invalid.
Teste¶
Com Testing Library e user-event, no mínimo:
- enviar vazio → aparece a mensagem de cada campo obrigatório;
- preencher válido → a função de envio é chamada uma vez, com os dados validados;
- back devolve 409 → a mensagem aparece no campo.
Formulário sem teste de validação volta na review.
Sinais de que a regra furou¶
| Erro | Sinal |
|---|---|
| Validação na mão | if (!email.includes('@')) dentro do componente |
| Tipo duplicado | interface Form escrita ao lado de um z.object igual |
| Mensagem cravada | 'Campo obrigatório' no lugar da chave de i18n |
| Campo no Redux | dispatch(setNome(e.target.value)) |
| Erro do back no lugar errado | 409 virando aviso genérico com o campo intacto |