Reaccionar en 60 segundos: validación del formulario





¡Buen dia amigos!



En este pequeño tutorial, quiero mostrarte un ejemplo de validación de formulario cliente-servidor.



El cliente se implementará en React, el servidor en Express.



No reinventaremos la rueda, pero usaremos soluciones listas para usar: react-hook-form se usará para validar el formulario en el lado del cliente (+: se usan ganchos, ruso), y en el lado del servidor - express-validator . Los componentes con estilo (CSS-in-JS o All-in-JS, dado JSX)



se usarán para diseñar . El código fuente del ejemplo está aquí .







Puedes jugar con el código aquí .



Sin más preámbulos.



Cliente



Crea un proyecto usando create-react-app :



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

      
      





En el futuro, usaré yarn para instalar dependencias y ejecutar comandos.



Estructura del proyecto después de eliminar archivos innecesarios:



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

      
      





Instalar dependencias:



#  
yarn add styled-components react-hook-form

#   ( )
yarn add express express-validator cors

#   (  )
yarn add -D nodemon

#    
yarn add concurrently

      
      





Dado que los componentes con estilo no pueden importar fuentes, tendremos que agregarlas 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>

      
      





Nuestro formulario tendrá tres campos: un nombre de usuario, su dirección de correo electrónico y una contraseña. Condiciones que deben cumplir los datos:



  • Nombre

    • de 2 a 10 caracteres
    • cirílico


  • Email

    • ningún requerimiento especial


  • Contraseña

    • 8-12 caracteres
    • Latín: letras en cualquier caso, números, guion bajo y guion




Comencemos con el estilo (src / styles.js; para resaltar la sintaxis, uso la extensión 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 a importar e incluir estilos globales en 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')
)

      
      





Vaya al archivo principal del 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 />
    </>
  )
}

      
      





El método register () del gancho useForm () admite todos los atributos de la etiqueta de entrada. Una lista completa de tales atributos . En el caso de un nombre, podríamos limitarnos a una expresión regular.



Inicie el servidor para el cliente usando yarn start y pruebe la forma:







Genial. La validación del lado del cliente funciona como se esperaba. Pero siempre puedes apagarlo. Por tanto, es necesaria la validación en el servidor.



Servidor



Comencemos a implementar el 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}`)
})

      
      





Puede encontrar una lista completa de validadores disponibles aquí .



Agreguemos un par de scripts a package.json - "servidor" para iniciar el servidor y "dev" para iniciar los servidores simultáneamente:



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

      
      





Ejecutando el desarrollo de hilo y probando el envío del formulario:











Genial. Parece que lo hemos logrado.



Hemos cubierto una validación de formulario cliente-servidor muy simple. Al mismo tiempo, las opciones más complejas solo implican un aumento en el número de validadores, los principios generales siguen siendo los mismos. También vale la pena señalar que la validación de formularios del lado del cliente se puede implementar fácilmente usando HTML ( GitHub , CodeSandbox ).



Gracias por su atención y que tenga un buen día.



All Articles