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.