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

AWS GenAI LIVE image

How is generative AI increasing efficiency?

Join AWS GenAI LIVE! to find out how gen AI is reshaping productivity, streamlining processes, and driving innovation.

Learn more

Top comments (0)

AWS Q Developer image

Your AI Code Assistant

Ask anything about your entire project, code and get answers and even architecture diagrams. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Start free in your IDE

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay