Reagir em 60 segundos: validação do formulário





Bom dia amigos!



Neste pequeno tutorial, quero mostrar um exemplo de validação de formulário cliente-servidor.



O cliente será implementado no React, o servidor no Express.



Não reinventaremos a roda, mas usaremos soluções prontas: o react-hook-form será usado para validar o formulário no lado do cliente (+: ganchos são usados, russo) e no lado do servidor - express-validator . Componentes



estilizados (CSS-in-JS ou All-in-JS, determinado JSX) serão usados ​​para estilização . O código-fonte do exemplo está aqui .







Você pode brincar com o código aqui .



Sem mais preâmbulos.



Cliente



Crie um projeto usando create-react-app :



yarn create react-app form-validation
# 
npm init react-app form-validation
# 
npx create-react-app form-validation

      
      





No futuro, usarei o yarn para instalar dependências e executar comandos.



Estrutura do projeto após a remoção de arquivos desnecessários:



public
  index.html
src
  App.js
  index.js
  styles.js
server.js
...

      
      





Instale dependências:



#  
yarn add styled-components react-hook-form

#   ( )
yarn add express express-validator cors

#   (  )
yarn add -D nodemon

#    
yarn add concurrently

      
      





Como os componentes estilizados não podem importar fontes, teremos que adicioná-los a public / index.html:



<head>
  ...
  <link rel="preconnect" href="https://fonts.gstatic.com" />
  <link
    href="https://fonts.googleapis.com/css2?family=Comfortaa&display=swap"
    rel="stylesheet"
  />
</head>

      
      





Nosso formulário terá três campos: um nome de usuário, seu endereço de e-mail e uma senha. Condições que os dados devem satisfazer:



  • Nome

    • de 2 a 10 caracteres
    • cirílico


  • O email

    • nenhum requesito especial


  • Senha

    • 8-12 caracteres
    • Latim: letras em qualquer caso, números, sublinhado e hífen




Vamos começar com o estilo (src / styles.js; para destaque de sintaxe, uso a extensão VSCode vscode-styled-components):



//  
import styled, { createGlobalStyle } from 'styled-components'

//  
const GlobalStyle = createGlobalStyle`
  body {
    margin: 0;
    min-height: 100vh;
    display: grid;
    place-items: center;
    background-color: #1c1c1c;
    font-family: 'Comfortaa', cursive;
    font-size: 14px;
    letter-spacing: 1px;
    color: #f0f0f0;
  }
`

// 
const StyledTitle = styled.h1`
  margin: 1em;
  color: orange;
`

// 
const StyledForm = styled.form`
  margin: 0 auto;
  width: 320px;
  font-size: 1.2em;
  text-align: center;
`

// 
const Label = styled.label`
  margin: 0.5em;
  display: grid;
  grid-template-columns: 1fr 2fr;
  align-items: center;
  text-align: left;
`

//     
const BaseInput = styled.input`
  padding: 0.5em 0.75em;
  font-family: inherit;
  font-size: 0.9em;
  letter-spacing: 1px;
  outline: none;
  border: none;
  border-radius: 4px;
`

//  
const RegularInput = styled(BaseInput)`
  background-color: #f0f0f0;
  box-shadow: inset 0 0 2px orange;

  &:focus {
    background-color: #1c1c1c;
    color: #f0f0f0;
    box-shadow: inset 0 0 4px yellow;
  }
`

//      
const SubmitInput = styled(BaseInput)`
  margin: 1em 0.5em;
  background-image: linear-gradient(yellow, orange);
  cursor: pointer;

  &:active {
    box-shadow: inset 0 1px 3px #1c1c1c;
  }
`

//    
const BaseText = styled.p`
  font-size: 1.1em;
  text-align: center;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);
`

//   
const ErrorText = styled(BaseText)`
  font-size: ${(props) => (props.small ? '0.8em' : '1.1em')};
  color: red;
`

//   
const SuccessText = styled(BaseText)`
  color: green;
`

//   
export {
  GlobalStyle,
  StyledTitle,
  StyledForm,
  Label,
  RegularInput,
  SubmitInput,
  ErrorText,
  SuccessText
}

      
      





Vamos importar e incluir estilos globais em src / index.js:



import React from 'react'
import ReactDOM from 'react-dom'

//   
import { GlobalStyle } from './styles'

import App from './App'

ReactDOM.render(
  <React.StrictMode>
    {/*    */}
    <GlobalStyle />
    <App />
  </React.StrictMode>,
  document.getElementById('root')
)

      
      





Vá para o arquivo principal do cliente (src / App.js):



import { useState } from 'react'
//     
import { useForm } from 'react-hook-form'

//   
import {
  StyledTitle,
  StyledForm,
  Label,
  RegularInput,
  SubmitInput,
  ErrorText,
  SuccessText
} from './styles'

//  
function Title() {
  return <StyledTitle> </StyledTitle>
}

//  
function Form() {
  //   
  const [result, setResult] = useState({
    message: '',
    success: false
  })

  //   :
  //   
  //     
  const { register, errors, handleSubmit } = useForm()

  //  
  const validators = {
    required: '   '
  }

  //   
  async function onSubmit(values) {
    console.log(values)

    const response = await fetch('http://localhost:5000/server', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(values)
    })

    const result = await response.json()

    //  
    setResult({
      message: result,
      success: response.ok
    })
  }

  //           
  function onClick() {
    window.location.reload()
  }

  return (
    <>
      <StyledForm onSubmit={handleSubmit(onSubmit)}>
        <Label>
          :
          <RegularInput
            type='text'
            name='name'
            //   
            //   
            ref={register({
              ...validators,
              minLength: {
                value: 2,
                message: '   '
              },
              maxLength: {
                value: 10,
                message: '   '
              },
              pattern: {
                value: /[-]{2,10}/i,
                message: ' '
              }
            })}
            defaultValue=''
          />
        </Label>
        {/*  */}
        <ErrorText small>{errors.name && errors.name.message}</ErrorText>

        <Label>
          Email:
          <RegularInput
            type='email'
            name='email'
            ref={register({
              ...validators,
              pattern: {
                value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
                message: '   '
              }
            })}
            defaultValue='email@example.com'
          />
        </Label>
        <ErrorText small>{errors.email && errors.email.message}</ErrorText>

        <Label>
          :
          <RegularInput
            type='password'
            name='password'
            ref={register({
              ...validators,
              pattern: {
                value: /^[A-Z0-9_-]{8,12}$/i,
                message:
                  ' 8  12 : , ,    '
              }
            })}
            defaultValue='password'
          />
        </Label>
        <ErrorText small>
          {errors.password && errors.password.message}
        </ErrorText>

        <SubmitInput type='submit' defaultValue='' />

        {/*     "as",    ""      */}
        <SubmitInput as='button' onClick={onClick}>
          
        </SubmitInput>
      </StyledForm>

      {/*    */}
      {result.success ? (
        <SuccessText>{result.message}</SuccessText>
      ) : (
        <ErrorText>{result.message}</ErrorText>
      )}
    </>
  )
}

export default function App() {
  return (
    <>
      <Title />
      <Form />
    </>
  )
}

      
      





O método register () do gancho useForm () suporta todos os atributos da tag de entrada. Uma lista completa de tais atributos . No caso de um nome, podemos nos limitar a uma expressão regular.



Inicie o servidor para o cliente usando o yarn start e teste o formulário:







Ótimo. A validação do lado do cliente funciona conforme o esperado. Mas você sempre pode desligá-lo. Portanto, a validação no servidor é necessária.



Servidor



Vamos começar a implementar o servidor (server.js):



const express = require('express')
// body   
// validationResult -  
const { body, validationResult } = require('express-validator')
const cors = require('cors')

const app = express()
const PORT = process.env.PORT || 5000

app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: false }))

// 
const validators = [
  body('name').trim().notEmpty().isAlpha('ru-RU').escape(),
  body('email').normalizeEmail().isEmail(),
  //  
  body('password').custom((value) => {
    const regex = /^[A-Z0-9_-]{8,12}$/i

    if (!regex.test(value)) throw new Error('   ')

    return true
  })
]

//     middleware
app.post('/server', validators, (req, res) => {
  //       
  const { errors } = validationResult(req)

  console.log(errors)

  //       
  if (errors.length) {
    res.status(400).json(' ')
  } else {
    res.status(201).json('  ')
  }
})

app.listen(PORT, () => {
  console.log(` . : ${PORT}`)
})

      
      





Uma lista completa de validadores disponíveis pode ser encontrada aqui .



Vamos adicionar alguns scripts a package.json - "server" para iniciar o servidor e "dev" para iniciar os servidores simultaneamente:



"scripts": {
  "start": "react-scripts start",
  "build": "react-scripts build",
  "server": "nodemon server",
  "dev": "concurrently \"yarn server\" \"yarn start\""
}

      
      





Executando yarn dev e testando o envio do formulário:











Ótimo. Parece que conseguimos.



Cobrimos uma validação de formulário cliente-servidor muito simples. Ao mesmo tempo, opções mais complexas envolvem apenas um aumento no número de validadores, os princípios gerais permanecem os mesmos. Também é importante notar que a validação de formulário do lado do cliente pode ser facilmente implementada usando HTML ( GitHub , CodeSandbox ).



Obrigado pela atenção e tenha um bom dia.



All Articles