Reputation: 11
I'm developing a multistep form in ReactJs with Bootstrap, using Formik and Yup. Actually, I have 2 problems here, despite I've read part of Formik and Yup documentation, I haven't found the answer yet.
I change my select fields' values, but the validationSchema simply ignores these values and only the initialValues are submitted to this validation.
Before validating my fields' values through validationSchema, I have to check whether the select option is not the first one (this first option is equivalent to "choose your option" for each field, and when the user submits it, it's value should be empty for the schema), it's possible to be done without validationSchema, by creating a validate function out of Formik, but I want to use Formik because I want a prettier way to display the errors as it does. I've tried plenty of stuff around, but I haven't gotten any answer until now.
Here is the Step 1 Form Code**
import React, { Component } from 'react';
import Form from 'react-bootstrap/Form';
import {ErrorMessage, Formik, validateYupSchema} from "formik";
import Col from 'react-bootstrap/Col';
import InputGroup from 'react-bootstrap/InputGroup';
import Button from 'react-bootstrap/Button';
import * as yup from 'yup';
import PropTypes from 'prop-types';
import {Categories} from './categoriesList.jsx';
import {Brands} from './brandsList';
import {ProductTypes} from './productTypesList';
import NewProduct from './newProductComponent.jsx';
let schema = yup.object().shape({
productCategory: yup.string().required(),
productBrand: yup.string().required(),
productType: yup.string().required()
});
class FormProducts1 extends Component {
reset = () => {
this.props.handleReset1();
}
render() {
const {values, handleChange, handleReset1} = this.props;
const CategoryOptions = Categories,
MakeItemCategory = function(itemCategory) {
return <option value={itemCategory} key={itemCategory}>{itemCategory}</option>;
};
const BrandOptions = Brands,
MakeItemBrand = function(itemBrand) {
return <option value={itemBrand} key={itemBrand}>{itemBrand}</option>;
};
const ProductOptions = ProductTypes,
MakeItemProduct = function(itemProduct) {
return <option value={itemProduct} key={itemProduct}>{itemProduct}</option>;
};
return (
<Formik
initialValues = {{
productCategory:'',
productBrand: '',
productType: ''
}}
validationSchema = {schema}
onSubmit = {
values => {console.log(values)}
}
>
{({
handleSubmit,
handleBlur,
touched,
isInvalid, //defines what is invalid
errors //catch up the errors to be displayed
}) => (
<Form onSubmit={handleSubmit}>
<Form.Group as={Col} md="12" controlId="formProduct.ProductCategory">
<Form.Label>Categoria do Produto</Form.Label>
<Form.Control
name="productCategory"
as="select"
onChange={handleChange('productCategory')}
value={values.productCategory}
onBlur={() => handleBlur('productCategory',true)}
isInvalid={touched.productCategory && errors.productCategory}
>
{CategoryOptions.map(MakeItemCategory)}
</Form.Control>
<Form.Control.Feedback type="invalid">{errors.productCategory}</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="12" controlId="formProduct.ProductBrand">
<Form.Label>Marca do Produto</Form.Label>
<Form.Control
name="productBrand"
as="select"
onChange={handleChange('productBrand')}
value={values.productBrand}
onBlur = {() => handleBlur('productBrand',true)}
isInvalid = {touched.productBrand && errors.productBrand} >
{BrandOptions.map(MakeItemBrand)}
</Form.Control>
<Form.Control.Feedback type="invalid">{errors.productBrand}</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="12" controlId="formProduct.ProductType">
<Form.Label>Tipo do Produto</Form.Label>
<Form.Control
name="productType"
as="select"
onChange={handleChange('productType')}
value={values.productType}
onBlur = {() => handleBlur('productType',true)}
isInvalid={touched.productType && errors.productType}>
{ProductOptions.map(MakeItemProduct)}
</Form.Control>
<Form.Control.Feedback type="invalid">{errors.productType}</Form.Control.Feedback>
</Form.Group>
<Form.Group controlId="buttonsCategory" style={this.formatForm}>
<Button variant="secondary m-2" type="reset" style={this.styleAddButton} onClick={this.reset}>
Limpar
</Button>
<Button variant="primary" type="submit" style={this.styleAddButton}>
Continuar
</Button>
</Form.Group>
</Form>
)}
</Formik>
);
}
}
export default FormProducts1;
*****************Here is the Steps' Container Component Code *******************
import React, { Component } from 'react';
import FormProducts1 from './formProducts1';
import FormProducts2 from './formProducts2';
import FormProducts3 from './formProducts3';
import FormProductsSuccess from './formProductsSuccess';
import PropTypes from "prop-types";
class NewProduct extends Component {
state = {
step:1,
productCategory:'',
productBrand:'',
productType:'',
productName:'',
productPrice:0,
productCode:'',
productDescription:''
}
formatForm = {
marginTop:15
};
styleAddButton = {
fontSize:15,
fontWeight:"bold"
};
//Proceed to the next step
nextStep = () => {
const {step} = this.state
this.setState({
step: step + 1
});
}
//Go back to previous step <div>
prevStep = () => {
const {step} = this.state
this.setState({
step: step - 1
});
}
//Handle fields change
handleChange = input => e => {
this.setState({
[input]: e.target.value
});
}
toCurrency(number) {
const formatter = new Intl.NumberFormat("pt-BR", {
style: "decimal",
currency: "BRL"
});
return formatter.format(number);
}
handleReset1 = () => {
this.setState({
productCategory:'',
productBrand:'',
productType:''
});
}
handleReset2 = () => {
this.setState({
productName:'',
productPrice:0,
productCode:'',
productDescription:''
});
}
render() {
const {step} = this.state;
const {productCategory, productBrand, productType, productName, productPrice, productCode, productDescription} = this.state;
const values = {productCategory, productBrand, productType, productName, productPrice, productCode, productDescription};
switch(step){
case 1:
return (
<FormProducts1 nextStep = {this.nextStep}
handleChange = {this.handleChange} handleReset1 = {this.handleReset1}
values={values}
/>
)
case 2:
return(
<FormProducts2 nextStep = {this.nextStep}
prevStep = {this.prevStep} handleChange = {this.handleChange}
handleReset2 = {this.handleReset2}
values={values} toCurrency = {this.toCurrency}
/>
)
case 3:
return(
<FormProducts3 nextStep = {this.nextStep} prevStep = {this.prevStep}
values={values} toCurrency = {this.toCurrency}
/>
)
case 4:
return(
<FormProductsSuccess/>
)
}
}
}
NewProduct.propTypes = {
value: PropTypes.string,
onChange: PropTypes.func
};
export default NewProduct;
Thanks!
Upvotes: 1
Views: 1123
Reputation: 253
It took two days to make it error-free... and I had hoped this would be work at your end..
Here, you can find react-bootstrap all input(form-control) along with formik and yup validation package
import React from 'react';
import { Container, Row, Col, Image, Form, Button } from 'react-bootstrap';
import style from '../styles/Contact.module.css';
import { Formik, Field} from 'formik';
import * as Yup from 'yup';
const dropdown=[
{
key:"Select an option",
value:""
},
{
key:"Option 1",
value:"option1"
},
{
key:"Option 2",
value:"option2"
},
{
key:"Option 3",
value:"option3"
}
]
// RegEx for phone number validation
const phoneRegExp = /^(\+?\d{0,4})?\s?-?\s?(\(?\d{3}\)?)\s?-?\s?(\(?\d{3}\)?)\s?-?\s?(\(?\d{4}\)?)?$/
// Schema for yup
const validationSchema = Yup.object().shape({
name: Yup.string()
.min(2, "*Names must have at least 2 characters")
.max(30, "*Names can't be longer than 30 characters")
.required("*Name is required"),
email: Yup.string()
.email("*Must be a valid email address")
.max(100, "*Email must be less than 100 characters")
.required("*Email is required"),
phone: Yup.string()
.min(10, "*Names can't be longer than 10 numbers")
.matches(phoneRegExp, "*Phone number is not valid")
.required("*Phone number required"),
msg: Yup.string()
.min(2, "*Messages must have at least 2 characters")
.max(250, "*Messages can't be longer than 250 characters")
.required("*Messages is required"),
selectionOption: Yup.string()
// .of(Yup.string())
// .min(1)
.required('Required'),
});
const Signup = () => {
return (
<>
<Formik
initialValues={{ name: "", email: "", phone: "", msg: "",selectionOption:"" }}
validationSchema={validationSchema}
onSubmit={(values, { setSubmitting, resetForm }) => {
// When button submits form and form is in the process of submitting, submit button is disabled
console.log(values)
setSubmitting(true);
// Simulate submitting to database, shows us values submitted, resets form
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
resetForm();
setSubmitting(false);
}, 500);
}}
>
{({ values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting }) => (
<Form className="form" onSubmit={handleSubmit} autoComplete="off" name="contact" method="POST" >
<Row className="mb-5">
<Col lg={6} md={6} sm={12}>
<Form.Group controlId="formName">
<Form.Label className="form_label" >FullName</Form.Label>
<Form.Control
type="text"
name="name"
placeholder="Full Name"
onChange={handleChange}
onBlur={handleBlur}
value={values.name}
className={touched.name && errors.name ? "has-error" : null}
/>
{touched.name && errors.name ? (
<div className="error-message">{errors.name}</div>
) : null}
</Form.Group>
</Col>
<Col lg={6} md={6} sm={12}>
<Form.Group>
<Form.Label className="form_label" >Number</Form.Label>
<Form.Control
type="text"
name="phone"
placeholder="Phone"
onChange={handleChange}
onBlur={handleBlur}
value={values.phone}
className={touched.phone && errors.phone ? "has-error" : null}
/>
{touched.phone && errors.phone ? (
<div className="error-message">{errors.phone}</div>
) : null}
</Form.Group>
</Col>
</Row>
<Row className="mb-5">
<Col lg={6} md={6} sm={12}>
<Form.Group>
<Form.Label className="form_label" >Email</Form.Label>
<Form.Control
type="text"
name="email"
placeholder="Email"
onChange={handleChange}
onBlur={handleBlur}
value={values.email}
className={touched.email && errors.email ? "has-error" : null}
/>
{touched.email && errors.email ? (
<div className="error-message">{errors.email}</div>
) : null}
</Form.Group>
</Col>
<Col lg={6} md={6} sm={12}>
{/* <Form.Select aria-label="Default select example">
<option>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</Form.Select> */}
<Form.Group controlId="exampleForm.ControlSelect1">
<Form.Label>Example select</Form.Label>
<Form.Control as="select" name ="selectionOption" onChange={handleChange}
onBlur={handleBlur} value={values.selectionOption}
className={touched.selectionOption && errors.selectionOption ? "has-error" : null}
>
{
dropdown.map(drop=>{return(
<option key={drop.value} value={drop.value}>
{drop.key}
</option>
)
}
)
}
</Form.Control>
{/* <ErrorMessage name="selectionOption"></ErrorMessage> */}
{touched.selectionOption && errors.selectionOption ? (
<div className="error-message">{errors.selectionOption}</div>
) : null}
{/* <Form.Label>Example select</Form.Label>
<Field as="select" name ="selectionOption" onChange={handleChange}
onBlur={handleBlur} value={values.selectionOption}
style={{ display: "block" }}
// isValid={touched.selectionOption && !errors.selectionOption}
// isInvalid={!errors.selectionOption}
className={touched.selectionOption && errors.selectionOption ? "has-error" : null}
>
<option className='text-muted'>---</option>
<option value="ortho">ortho</option>
<option value="pedri">pedri</option>
<option value="crown">crown</option>
</Field>
{touched.selectionOption && errors.selectionOption ? (
<div className="error-message">{errors.selectionOption}</div>
) : null} */}
</Form.Group>
</Col>
</Row>
<Row className="mb-5">
<Col lg={12} md={12} sm={12}>
<Form.Group controlId="formmsg">
<Form.Label>Messages :</Form.Label>
<Form.Control
type="text"
name="msg"
as="textarea" rows={4}
placeholder="Query / Feedback"
onChange={handleChange}
onBlur={handleBlur}
value={values.msg}
className={touched.msg && errors.msg ? "has-error" : null}
/>
{touched.msg && errors.msg ? (
<div className="error-message">{errors.msg}</div>
) : null}
</Form.Group>
</Col>
</Row>
<Button type="submit" className={style.customizeBtn} name="contact" id="contact" disabled={isSubmitting}>
<Image src="img/send.png" className={style.imgBtn} />
<span className={style.titleBtn}>Submit</span>
</Button>
</Form>
)}
</Formik>
</>
)
}
export default Signup;
Upvotes: 0