DEV Community

Cover image for ⚛️⏳Parte 3: Criando um Timer com Histórico em React
Dev Maiqui 🇧🇷
Dev Maiqui 🇧🇷

Posted on

⚛️⏳Parte 3: Criando um Timer com Histórico em React

Seguindo com a terceira parte do projeto que construí na formação React da Rocketseat, um projeto de duas páginas/telas, onde uma tela contém o timer, e a outra tela contém o histórico dos ciclos realizados.

Nesta terceira parte do projeto vamos focar no formulário, validação e desempenho.

Caso queira adquirir os cursos da Rocketseat com o meu cupom de desconto Acesse esse link

Links úteis:

Capítulos:


1 - Controlled vs Uncontrolled

Neste conteúdo vamos entender como podemos criar bons formulários em React.

“form” da página home com botão desabilitado

Olhando o nosso formulário da página Home temos o campo onde colocamos o nome da tarefa e outro campo onde informamos por quanto tempo iremos trabalhar nessa tarefa. E, a primeira coisa que precisamos é de validação nesses campos, para que o usuário não faça o submit desse formulário sem preencher esses campos, ou preenchendo com o valor errado.

Outra coisa é o botão que fica ativo somente quando todos os campos estiverem preenchidos. Então, precisamos monitorar de alguma forma se o usuário está preenchendo, se já preencheu esses campos, podemos dizer em tempo real, para só depois habilitar o botão.

Há duas formas de trabalhar com formulários, a forma controlada e a forma não controlada (Controlled / Uncontrolled), e é muito importante entender esses dois termos, porque eles têm muito a ver como o React funciona.

A forma controlada (Controlled) é sobre manter em tempo real o estado, a informação que o usuário insere na nossa aplicação numa variável do componente. Então há todo momento que o usuário estiver digitando no input, o estado é atualizado com a nova informação, assim sempre teremos o valor mais atual do que o usuário digitou.

Estamos falando de formulário, mas isso pode ser usado em qualquer lugar que tenha um input.

Um exemplo fazendo da forma controlada:

usando o onchange no TaskInput

Como temos esse valor em tempo real, a gente consegue facilmente ter acesso a esse valor no momento de fazer o submit. E, facilmente, conseguimos refletir visualmente alterações na interface, baseado no valor desses inputs.

Um exemplo disso é quando queremos habilitar o botão apenas quando o valor da tarefa esteja preenchido:

Beneficio do input controlado

Então Controlled Components dentro do React, ou seja, qualquer componente que seja monitorado em tempo real o input do usuário, traz muita fluidez para mostrar ou deixar de mostrar algo na interface da aplicação, baseado nesse input do usuário.

O lado negativo do componente controlado

Toda vez que a gente faz uma atualização de estado, a gente provoca uma nova renderização, ou seja, toda vez que o setTask é chamado por qualquer motivo, o React precisa recalcular todo o conteúdo do componente, do estado que mudou. Isso de recalcular todo o conteúdo do componente, não necessariamente é lento, mas se a interface for muito complexa, com muita informação, pode, sim, virar um gargalo.

Então em alguns casos, lidar com formulários desta maneira controlada, pode ser um problema para a aplicação em questão de desempenho. Na maioria das vezes não vai ser um problema, mas em alguns momentos, vai ser.

A forma Uncontrolled

Como seria então a forma Uncontrolled?

A forma  raw `Uncontrolled` endraw

Desta forma Uncontrolled a gente acaba perdendo a fluidez.

Quando é indicado cada?

  • Controlled (Monitorado em tempo real):

    • formulários simples com poucos campos;
    • interface simples;
    • formulário de login, por exemplo;
  • Uncontrolled (Não Monitorado):

    • formulários gigantes com muitos campos;

2 - O melhor dos dois mundos com React Hook Form

Nessa parte vamos utilizar a biblioteca React Hook Form. Ela nos ajuda a trabalhar com Controlled Components sem perder o desempenho, evitando renderizações desnecessárias.

O React Hook Form é uma biblioteca popular para gerenciar formulários em React por várias razões importantes:

  1. Performance otimizada - Minimiza a quantidade de re-renderizações usando um sistema de registro de campos não controlados, o que melhora significativamente a performance comparado com formulários controlados tradicionais.

  2. Menos código - Reduz significativamente a quantidade de código necessário para gerenciar formulários. Você não precisa criar estados para cada campo nem gerenciar suas atualizações manualmente.

  3. Validação eficiente - Oferece validação integrada e fácil de implementar, incluindo:

    • Validação em tempo real
    • Validação personalizada
    • Integração com bibliotecas como Yup, Zod ou Joi
  4. Melhor experiência de desenvolvimento - Fornece:

    • Tipagem forte com TypeScript
    • DevTools para debugar
    • Tratamento de erros intuitivo
  5. Gerenciamento de estados do formulário - Gerencia automaticamente estados como:

    • touched/untouched
    • dirty/pristine
    • validação
    • submissão

Para fazer a instalação execute o comando em seu terminal:

$ npm i react-hook-form
Enter fullscreen mode Exit fullscreen mode

commit: build: ➕ add react-hook-form lib $ npm i react-hook-form


3 - Usando React Hook Form

No arquivo src/pages/Home/index.tsx vamos importar a função useForm:

import { useForm } from 'react-hook-form'
Enter fullscreen mode Exit fullscreen mode

useForm é um hook. Por convenção, um hook começa com use no nome. Os hooks são funções que acoplam uma funcionalidade em um componente existente.

useForm() retorna um objeto, então conseguimos fazemos a desestruturação desse objeto, e as funções principais são register e handleSubmit:

export function Home() {
  const { register, handleSubmit } = useForm()
}
Enter fullscreen mode Exit fullscreen mode

register é uma função que vai adicionar um input ao nosso formulário. useForm() é como se a gente tivesse criado um novo formulário para a aplicação. E a função register diz quais os campos que vamos ter no nosso formulário.

Para registrar o TaskInput:

<TaskInput
 id="task"
 list="task-suggestions"
 placeholder="Dê um nome para o seu projeto"
 {...register("task")}
/>
Enter fullscreen mode Exit fullscreen mode

A função register recebe o nome do input como argumento/valor e retorna vários métodos/funções e propriedades usadas no HTML:

sugestões de autocomplete do vscode para o campo register

Então com o spread operator {...register("task")} estamos pegando todos os métodos e as propriedades do register e passando para o componente TaskInput.

E o mesmo para o MinutesAmountInput:

<MinutesAmountInput
  type="number"
  id="minutesAmount"
  placeholder="00"
  step={5}
  min={5}
  max={60}
  {...register("minutesAmount", { valueAsNumber: true })}
/>
Enter fullscreen mode Exit fullscreen mode

{ valueAsNumber: true } é para transformar de string para number.

Usando handleSubmit

usando handleSubmit

Habilitando o botão (usando o watch)

Com a função watch podemos observar o campo e saber em tempo real o valor dele:

usando watch

Usando variáveis auxiliares

Variáveis auxiliares são variáveis que não alteram a funcionalidade do código, mas melhoram a legibilidade do código.

const isSubmitDisabled = !task

disabled={isSubmitDisabled}

O commit desta parte infelizmente eu acabei deixando junto com a parte 5 e a parte 6 😕 recomendo fazer a separação 😅

commit: feat: ✨ add controlled form with react-hook-form and validation with zod


4 - Validação com Zod

Por padrão, a biblioteca React Hook Form não traz nenhuma funcionalidade de validação. Ela prefere ser uma biblioteca enxuta, com menos funcionalidades, assim o usuário consegue escolher bibliotecas complementares de sua preferência, como, por exemplo, bibliotecas de validação de formulários baseada em esquema, já que existem ótimas opções como: Yup, Zod, Superstruct e Joi.

Aqui vamos utilizar a Zod, pois traz mais integração com TypeScript:

$ npm i zod
Enter fullscreen mode Exit fullscreen mode

commit: build: ➕ add validation lib $ npm i zod

E para o React Hook Form fazer a integração com essas bibliotecas de validação, precisamos instalar o pacote @hookform/resolvers:

$ npm i @hookform/resolvers
Enter fullscreen mode Exit fullscreen mode

commit: build: ➕ add lib to integrate react-hook-form with other libs $ npm i @hookform/resolvers


5 - Validação com o botão habilitado

Podemos ver na imagem abaixo que o botão fica desabilitado quando o campo está vazio:

botão desabilitado quando o campo está vazio

Neste caso, não precisaríamos de validação, mas como em muitos formulários não acontecem isso e necessitam informar ao usuário qual informação está incorreta, vamos habilitar o botão e realizar a validação do formulário.

No arquivo src/pages/Home/index.tsx vamos fazer a importação do Zod:

import { zodResolver } from "@hookform/resolvers/zod";
import * as zod from "zod";
Enter fullscreen mode Exit fullscreen mode

O código * as zod é uma técnica do ECMAScript Modules usado para importar tudo do pacote zod, já que ele não possui export default.

Antes de utilizar o zod, podemos ver no console.log(data) que adicionamos anteriormente neste mesmo arquivo, o formato do data como sendo um objeto:

código com o  raw `console.log(data)` endraw

formato de objeto aparecendo no console

No mesmo arquivo vamos realizar a validação então de um objeto com zod.object():

utilizando o  raw `zod.object()` endraw

Vamos verificar a validação dos minutos com o Zod, mas para fazer isso, precisamos comentar o código HTML que já faz essa validação:

validação do HTML

comentando a validação do HTML

Agora podemos testar a validação do Zod. Quando clicamos no botão, percebemos que nenhum resultado aparece no console do navegador, ou seja, não está chegando no console.log pois estamos com erros de validação:

Image description

Visualizando os erros de validação do Zod

Para visualizar os erros de validação do Zod, temos que usar o formState.

Perceba que o console.log(formState.errors) está fora da função handleCreateNewCycle:

Image description

Assim já conseguimos visualizar a mensagem de erro:

mensagem de erro do zod aparecendo no console do navegador

Caso eu queira colocar uma mensagem diferente:

mensagens de erro personalizadas

Essas mensagens podem ser mostradas em tela para o usuário, mas nesse projeto, como temos a validação do campo númerico pelo HTML e o botão que só habilita quando há algo no campo de descrição, não iremos precisar mostrar essas mensagens ao usuário.

Assim podemos remover o formState e colocar o max={60} devolta.

O commit desta parte infelizmente eu acabei deixando junto com a parte 3 e parte 6 😕 recomendo fazer a separação 😅

commit: feat: ✨ add controlled form with react-hook-form and validation with zod


6 - TypeScript no Formulário

Agora vamos colocar uma tipagem para o data: any como mostrado na imagem abaixo:

data sem tipagem

Podemos já deduzir qual será a tipagem olhando para o nosso formulário, um objeto; com esses dois campos:

olhando o código do formulario

Podemos criar a tipagem de forma manual com interface:

criando tipagem de forma manual com interface

Existe uma propriedade chamada de defaultValues que podemos passar pra o objeto de configuração dentro do useForm, que nos permite setar valores iniciais para os campos:

 raw `defaultValues` endraw  com valores iniciais nos campos

Se tentarmos usar o comando de autocomplete Ctrl + Space, não há sugestões válidas:

sem autocomplete no  raw `defaultValues` endraw

Para obtermos o autocomplete precisamos usar um generic, como mostrado quando paramos o mouse em cima do useForm, assim visualizamos os valores padrão <{}, any>:

mostrando generic do useForm

Adicionando o generic obtemos o autocomplete:

adicionando o  raw `generic` endraw  no  raw `useForm` endraw

Tipagem automática por inferência

Olhando para o código abaixo jé podemos perceber os tipos pela estrutura:

newCycleFormValidationSchema

Usando infer do TypeScript conseguimos extrair a tipagem do zod.object() e, assim, podemos remover o interface:

inferencia de tipo usando infer

Note que usamos a palara type, podemos assumir interface quando criamos o tipo manualmente, e type quando o tipo vem de forma automática.

Qual o benefício de usar o infer?

Se no futuro precisássemos adicionar um campo novo, por exemplo, chamado de owner, automaticamente o infer adicionaria o tipo do campo. Parando o mouse em cima de NewCycleFormData podemos notar o campo novo adicionado automaticamente:

adicionando o campo owner como exemplo

Isso é o diferencial do Zod e você pode conferir tudo isso na documentação oficial da bilioteca.

O commit da parte 6 infelizmente eu acabei deixando junto com a parte 3 e parte 5 😕 recomendo fazer a separação 😅

feat: ✨ add controlled form with react-hook-form and validation with zod


7 - Resetando o Formulário

Ao iniciar o timer clicando no botão Começar, os campos não estão sendo resetados para o valor original. A biblioteca React Hook Form fornece a função reset para resetar os campos para os valores originais do defaultValues:

função  raw `reset` endraw  do  raw `React Hook Form` endraw

Top comments (0)