In 60 Sekunden reagieren: Formularvalidierung





Guten Tag, Freunde!



In diesem kleinen Tutorial möchte ich Ihnen ein Beispiel für die Validierung von Client-Server-Formularen zeigen.



Der Client wird in React implementiert, der Server in Express.



Wir werden das Rad nicht neu erfinden, sondern vorgefertigte Lösungen verwenden: React-Hook-Form wird verwendet, um das Formular auf der Client-Seite (+: Hooks werden verwendet, Russisch) und auf der Serverseite zu validieren - Express-Validator .



Für das Styling werden gestaltete Komponenten (CSS-in-JS oder All-in-JS bei JSX) verwendet .



Der Quellcode für das Beispiel ist hier .



Sie können mit dem Code spielen hier .



Ohne weitere Präambel.



Kunde



Erstellen Sie ein Projekt mit der Create -React-App :



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

      
      





In Zukunft werde ich Garn verwenden, um Abhängigkeiten zu installieren und Befehle auszuführen.



Projektstruktur nach dem Entfernen unnötiger Dateien:



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

      
      





Abhängigkeiten installieren:



#  
yarn add styled-components react-hook-form

#   ( )
yarn add express express-validator cors

#   (  )
yarn add -D nodemon

#    
yarn add concurrently

      
      





Da gestaltete Komponenten keine Schriftarten importieren können, müssen wir sie zu public / index.html hinzufügen:



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

      
      





Unser Formular enthält drei Felder: einen Benutzernamen, seine E-Mail-Adresse und ein Passwort. Bedingungen, die Daten erfüllen müssen:



  • Name

    • 2 bis 10 Zeichen
    • kyrillisch


  • Email

    • Keine besonderen Anforderungen


  • Passwort

    • 8-12 Zeichen
    • Lateinisch: Buchstaben auf jeden Fall, Zahlen, Unterstrich und Bindestrich




Beginnen wir mit dem Styling (src / styles.js; zur Hervorhebung der Syntax verwende ich die VSCode-Erweiterung 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
}

      
      





Importieren wir globale Stile und fügen sie in src / index.js ein:



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')
)

      
      





Wechseln Sie zur Hauptclientdatei (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 />
    </>
  )
}

      
      





Die register () -Methode des useForm () -Hooks unterstützt alle Attribute des Eingabe-Tags. Eine vollständige Liste solcher Attribute . Im Falle eines Namens könnten wir uns auf einen regulären Ausdruck beschränken.



Starten Sie den Server für den Client mit Garnstart und testen Sie das Formular:







Großartig. Die clientseitige Validierung funktioniert wie erwartet. Sie können es aber jederzeit ausschalten. Daher ist eine Validierung auf dem Server erforderlich.



Server



Beginnen wir mit der Implementierung des Servers (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}`)
})

      
      





Eine vollständige Liste der verfügbaren Validatoren finden Sie hier .



Fügen wir package.json ein paar Skripte hinzu - "Server" zum Starten des Servers und "dev" zum gleichzeitigen Starten der Server:



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

      
      





Garnentwicklung ausführen und Formularübermittlung testen:











Großartig. Es scheint, wir haben es geschafft.



Wir haben eine sehr einfache Validierung des Client-Server-Formulars behandelt. Gleichzeitig führen komplexere Optionen nur zu einer Erhöhung der Anzahl der Validatoren, die allgemeinen Grundsätze bleiben gleich. Es ist auch erwähnenswert, dass die clientseitige Formularvalidierung einfach mit HTML ( GitHub , CodeSandbox ) implementiert werden kann .



Vielen Dank für Ihre Aufmerksamkeit und einen schönen Tag.



All Articles