I´m creating a multi steps form on React. The steps are ok. I need to get it ready to backend, so I´m trying to use async function to validate de inputs.
This is my project structure:
- Signup (base code);
- Initial (step 1 - the user select "Pessoa Fisica" (natural person) or "Pessoa Juridica" (legal person);
- Pessoa Fisica (step 2 - If ok, go to step 4);
- Pessoa Juridica (step 3 - If ok, go to step 4);
- Success (step 4).
I´m trying to validate the step 2 first.
The base code of steps (Signup) is the following:
import React, { Component } from 'react';
import Initial from './_initial';
import PessoaFisica from './_pessoafisica';
import PessoaJuridica from './_pessoajuridica';
import Success from './_sucess';
export default class Signup extends Component {
state = {
step: 1,
email: '',
cpf: '',
cnpj: '',
datanascimento: '',
acceptterms: '',
acceptimage: '',
};
// go back to previous step
prevStep = () => {
const { step } = this.state;
this.setState( { step: step - 1 } );
};
superprevStep = () => {
const { step } = this.state;
this.setState( { step: step - 2 } );
};
// proceed to the next step
nextStep = () => {
const { step } = this.state;
this.setState( { step: step + 1 } );
};
supernextStep = () => {
const { step } = this.state;
this.setState( { step: step + 2 } );
};
// Handle fields change
handleChange = input => e => {
this.setState( { [ input ]: e.target.value } );
};
render() {
const { step } = this.state;
const { email, cpf, cnpj, datanascimento, acceptterms, acceptimage } = this.state;
const values = { email, cpf, cnpj, datanascimento, acceptterms, acceptimage };
switch ( step ) {
case 1:
return (
<Initial
nextStep={ this.nextStep }
supernextStep={ this.supernextStep }
/>
);
case 2:
return (
<PessoaFisica
prevStep={ this.prevStep }
supernextStep={ this.supernextStep }
handleChange={ this.handleChange }
values={ values }
/>
);
case 3:
return (
<PessoaJuridica
superprevStep={ this.superprevStep }
nextStep={ this.nextStep }
handleChange={ this.handleChange }
values={ values }
/>
);
case 4:
return ( <Success />
);
default:
}
}
}
The second step:
import React, { useState } from "react";
import Checkbox from '@mui/material/Checkbox';
import * as Yup from "yup";
const PessoaFisica = ( { prevStep, supernextStep, handleChange, values } ) => {
const Continue = e => {
e.preventDefault();
supernextStep();
};
const Previous = e => {
e.preventDefault();
prevStep();
};
const [ emailForm, setEmailForm ] = useState();
const [ cpfForm, setCpfForm ] = useState();
const [ birthday, setBirthday ] = useState();
const [ termsAccepted, setTermsAccepted ] = useState();
const [ checkboxStatus, setCheckboxStatus ] = useState( true );
function buttonHandler() {
setCheckboxStatus( !checkboxStatus );
}
function SubmitButton() {
if ( emailForm && cpfForm && birthday && termsAccepted ) {
return <button type="submit" disabled={ checkboxStatus } className="" onClick={ addUser }>Continuar</button>;
} else {
return <button type="submit" disabled className="">Continuar</button>;
};
};
const addUser = async e => {
e.preventDefault();
if ( !( await validate() ) ) return;
const saveDataForm = true;
if ( saveDataForm ) {
Continue;
} else {
setStatus( {
type: 'error',
mensagem: "Erro: Verifique os valores digitados e tente novamente!"
} );
}
};
async function validate() {
let schema = Yup.object().shape( {
cpf: Yup
.string()
.min( 11, ( { min } ) => `CPF deve conter no mínimo ${ min } caracteres` )
.required( 'CPF é obrigatório' ),
email: Yup
.string()
.email( "Por favor entre com um email válido" )
.required( 'Email é obrigatório' ),
datanascimento: Yup
.string()
.required( 'A data de nascimento é obrigatória. Você deve ser maior de 18 anos.' ),
.test( "DOB",
"error message",
value => {
return moment().diff( moment( value ), 'years' ) >= 18;
} )
} );
try {
await schema.validate( values );
return true;
} catch ( err ) {
setStatus( {
type: 'error',
mensagem: err.errors
} );
return false;
}
}
return (
<>
//The inputs:
<label htmlFor="email" className="">E-mail</label>
<input type="email" name="email" id="email" width="300" autoComplete="email" className="" onChange={ e => { handleChange( 'email' ); setEmailForm( e.target.value ); } } defaultValue={ values.email } required />
<label htmlFor="cpf" className="text-[16px] poppins mt-[24px] mb-[8px] text-black">CPF</label>
<input type="text" autoComplete="cpf" name="cpf" id="cpf" width="300" className="" onChange={ e => { handleChange( 'cpf' ); setCpfForm( e.target.value ); } } defaultValue={ values.cpf } required />
<label htmlFor="datanascimento" className="">Data de nascimento</label>
<input type="text" name="datanascimento" autoComplete="birthday" id="datanascimento" width="300" className="" placeholder="dd/mm/aaaa" onChange={ e => { handleChange( 'datanascimento' ); setBirthday( e.target.value ); } } defaultValue={ values.datanascimento } required />
//Other info here
//error message
{ status.type === 'error' ? <p className="">{ status.mensagem }</p> : null }
<SubmitButton />
//other info below
);
};
export default PessoaFisica;
But when I click the submit Button, it seems Yup don´t get the values. It throws a error message that the inputs are required, even when they aren´t empty. I need it to validate and then go to step 4.
EDIT
I changed everything. I ended up using Formik and material ui to validate Form. I changed the Signup file and everything is all right:
Signup.js
import React, { Component } from 'react';
import Initial from './_initial';
import PessoaFisica from './_pessoafisica';
import PessoaJuridica from './_pessoajuridica';
import Success from './_sucess';
export default class Signup extends Component {
state = {
step: 1,
};
// go back to previous step
prevStep = () => {
const { step } = this.state;
this.setState( { step: step - 1 } );
};
superprevStep = () => {
const { step } = this.state;
this.setState( { step: step - 2 } );
};
// proceed to the next step
nextStep = () => {
const { step } = this.state;
this.setState( { step: step + 1 } );
};
supernextStep = () => {
const { step } = this.state;
this.setState( { step: step + 2 } );
};
render() {
const { step } = this.state;
switch ( step ) {
case 1:
return (
<Initial
nextStep={ this.nextStep }
supernextStep={ this.supernextStep }
/>
);
case 2:
return (
<PessoaFisica
prevStep={ this.prevStep }
supernextStep={ this.supernextStep }
/>
);
case 3:
return (
<PessoaJuridica
superprevStep={ this.superprevStep }
nextStep={ this.nextStep }
/>
);
case 4:
return ( <Success />
);
default:
}
}
}
I´m using the Signup just do control steps.
The second step:
import React from "react";
import InputMask from "react-input-mask";
import Checkbox from "@mui/material/Checkbox";
import { TextField, Button } from "@mui/material";
import * as yup from "yup";
import { validateCPF } from "validations-br";
import { Form, Formik, ErrorMessage } from "formik";
import { parse } from "date-fns";
import { createTheme, ThemeProvider } from "@mui/material/styles";
const PessoaFisica = ( { prevStep, supernextStep } ) => {
const Previous = ( e ) => {
e.preventDefault();
prevStep();
};
const validationSchema = yup.object( {
cpf: yup
.string()
.transform( ( value ) => value.replace( /[^\d]/g, "" ) )
.min( 11, " O CPF deve conter no mínimo 11 caracteres" )
.test( "is-cpf", "O CPF não é válido", ( value ) => validateCPF( value ) )
.required( "O CPF é obrigatório" ),
email: yup
.string()
.email( "Por favor entre com um email válido" )
.required( "Email é obrigatório" ),
datanascimento: yup
.date()
.transform( ( value, originalValue, context ) => {
// check to see if the previous transform already parsed the date
if ( context.isType( value ) ) return value;
// Date parsing failed in previous transform
// Parse the date as a euro formatted date string or returns Invalid Date
return parse( originalValue, "dd/MM/yyyy", new Date() );
} )
.max(
new Date( Date.now() - 567648000000 ),
"Você precisa ter pelo menos 18 anos"
)
.required( "A data de nascimento é obrigatória" )
.typeError( "Digite uma data válida" ),
acceptterms: yup
.boolean()
.oneOf( [ true ], "Você precisa aceitar nossos Termos e Condições" ),
} );
return (
<>
//(...)
<Formik
validationSchema={ validationSchema }
validateOnMount
initialValues={ {
tipodepessoa: "Pessoa Física",
email: "",
cpf: "",
datanascimento: "",
acceptterms: false,
acceptimage: false,
} }
onSubmit={ ( values ) => {
alert( JSON.stringify( values, null, 2 ) );
supernextStep();
} }
>
{ ( formik ) => (
<Form
className="w-full h-full justify-between flex flex-col p-[30px_32px_16px] lg:p-[35px_32px_16px] box-border"
action=""
method="post"
onSubmit={ formik.handleSubmit }
>
//...
<TextField
name="email"
id="email"
width="300"
label="E-mail*"
placeholder="Digite um e-mail válido"
autoComplete="email"
onChange={ formik.handleChange }
value={ formik.values.email }
error={ formik.touched.email && Boolean( formik.errors.email ) }
helperText={ formik.touched.email && formik.errors.email }
/>
And so go on...