Formulários React: React Hook Form + Zod pra validação perfeita

Formulários React: React Hook Form + Zod para validação perfeita

Criar formulários em aplicações web é uma das tarefas mais comuns e, ao mesmo tempo, desafiadoras para desenvolvedores. Um dos maiores desafios é garantir que as entradas dos usuários sejam válidas e tratadas corretamente. Aqui, vou mostrar como usar o React Hook Form em conjunto com o Zod para criar formulários robustos e com validação de dados eficaz.

Setup básico

Para começar, precisamos configurar nosso projeto React. Se ainda não tem um projeto, você pode criar um usando o Create React App:

npx create-react-app meu-formulario
cd meu-formulario

Após criar o projeto, instale as dependências necessárias:

npm install react-hook-form zod @hookform/resolvers

Com as dependências instaladas, podemos começar a criar nosso formulário. Abaixo está um exemplo básico de um formulário com campos para nome e email:

import React from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';

const schema = z.object({
  nome: z.string().min(1, 'Nome é obrigatório.'),
  email: z.string().email('Email inválido.'),
});

const MeuFormulario = () => {
  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: zodResolver(schema),
  });

  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label>Nome:</label>
        <input type="text" {...register('nome')} />
        {errors.nome && <span>{errors.nome.message}</span>}
      </div>
      <div>
        <label>Email:</label>
        <input type="text" {...register('email')} />
        {errors.email && <span>{errors.email.message}</span>}
      </div>
      <button type="submit">Enviar</button>
    </form>
  );
};

export default MeuFormulario;

Neste exemplo, criamos um schema usando o Zod que valida se o nome não está vazio e se o email é válido. A função handleSubmit do React Hook Form lida com a submissão do formulário e, se houver erros, eles são exibidos abaixo dos campos.

Validação com Zod

A validação é uma parte crucial de qualquer formulário. Com o Zod, podemos definir regras de validação de maneira clara e concisa. Além de validar se os campos estão preenchidos, podemos adicionar regras mais complexas.

Por exemplo, vamos adicionar uma validação para um campo de senha:

const schema = z.object({
  nome: z.string().min(1, 'Nome é obrigatório.'),
  email: z.string().email('Email inválido.'),
  senha: z.string().min(6, 'A senha deve ter pelo menos 6 caracteres.'),
});

Com essa adição, agora o formulário também irá exigir que o usuário crie uma senha com pelo menos 6 caracteres. O gerenciamento de erros continua o mesmo, e os erros de validação serão exibidos da mesma forma.

Erros bonitos

A experiência do usuário é fundamental. Ao exibir mensagens de erro, precisamos garantir que elas sejam visíveis e intuitivas. Podemos estilizar as mensagens de erro para que se destaquem:

span {
  color: red;
  font-size: 0.8em;
}

Com essa adição simples, as mensagens de erro agora aparecem em vermelho, facilitando a identificação dos problemas para o usuário.

Multi-step forms

Formulários de múltiplas etapas são comuns em muitas aplicações. O React Hook Form facilita a implementação de formulários que são divididos em várias etapas. Vamos considerar um exemplo simples onde o usuário preenche informações básicas em uma etapa e detalhes adicionais em outra.

import React, { useState } from 'react';

const MultiStepForm = () => {
  const [step, setStep] = useState(1);
  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: zodResolver(schema),
  });

  const onSubmit = (data) => {
    if (step === 1) {
      setStep(2);
    } else {
      console.log(data);
    }
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {step === 1 && (
        <>
          <div>
            <label>Nome:</label>
            <input type="text" {...register('nome')} />
            {errors.nome && <span>{errors.nome.message}</span>}
          </div>
          <div>
            <label>Email:</label>
            <input type="text" {...register('email')} />
            {errors.email && <span>{errors.email.message}</span>}
          </div>
          <button type="submit">Próximo</button>
        </>
      )}
      {step === 2 && (
        <>
          <div>
            <label>Senha:</label>
            <input type="password" {...register('senha')} />
            {errors.senha && <span>{errors.senha.message}</span>}
          </div>
          <button type="submit">Enviar</button>
        </>
      )}
    </form>
  );
};

Neste exemplo, usamos um estado para manter o controle da etapa atual. Quando o usuário clica em "Próximo", mudamos para a próxima etapa e, ao final, enviamos os dados do formulário.

File uploads

Lidar com upload de arquivos é uma funcionalidade que pode ser necessária em muitos formulários. Com o React Hook Form, isso também é simples. Para adicionar um campo de upload de arquivos, podemos fazer o seguinte:

const schema = z.object({
  nome: z.string().min(1, 'Nome é obrigatório.'),
  email: z.string().email('Email inválido.'),
  arquivo: z.instanceof(File).refine(file => file.size < 5000000, 'O arquivo deve ter menos de 5MB.'),
});

Nesse exemplo, adicionamos um campo para arquivos e validamos se o arquivo não excede 5MB. O campo de upload pode ser adicionado ao formulário da seguinte maneira:

<div>
  <label>Arquivo:</label>
  <input type="file" {...register('arquivo')} />
  {errors.arquivo && <span>{errors.arquivo.message}</span>}
</div>

Não se esqueça de adicionar o atributo enctype="multipart/form-data" ao seu formulário, caso esteja lidando com uploads de arquivos.

Performance

Quando estamos lidando com formulários complexos e extensos, a performance pode se tornar uma preocupação. O React Hook Form é otimizado para lidar com grandes formulários, garantindo que apenas os campos que mudam sejam re-renderizados. Isso proporciona uma experiência mais suave para o usuário.

Além disso, você pode usar a opção shouldUnregister na configuração do useForm para garantir que os campos não utilizados não sejam mantidos na memória, o que pode ajudar na performance:

const { register, handleSubmit, formState: { errors } } = useForm({
  resolver: zodResolver(schema),
  shouldUnregister: true,
});

Conclusão

Aqui, exploramos como criar formulários eficientes e robustos em React usando o React Hook Form em combinação com o Zod para validação de dados. Vimos como configurar um formulário básico, implementar validação, tratar erros de forma amigável, criar formulários de múltiplas etapas, gerenciar uploads de arquivos e otimizar a performance.

Takeaways práticos

  • O React Hook Form é uma excelente ferramenta para gerenciamento de formulários.
  • O Zod facilita a validação de dados de forma clara e concisa.
  • Estilizar mensagens de erro melhora a experiência do usuário.
  • Formulários de múltiplas etapas podem ser implementados facilmente com controle de estado.
  • A performance do formulário pode ser otimizada com boas práticas.

Glossário do post

react
Biblioteca JavaScript para construir interfaces de usuário a partir de componentes reutilizáveis, mantida pela Meta.
Ismael Douglas

Sobre o autor

Ismael Douglas

Desenvolvedor Full Stack apaixonado por construir produtos web rápidos e bem projetados. Escrevo sobre o que aprendo no caminho — código, infra e boas práticas.

Veja meus projetos no portfólio →