Formik not displaying errors in React - reactjs

I am trying to get the validation errors to display on touch but for some reason they're not showing up. I've tried following the tutorial on the official Formik website, but to no avail. I am using React-Bootstrap, Formik, and Yup for validation. What am I doing wrong?
Imports:
import * as React from 'react';
import { Container, Row, Col, Form } from 'react-bootstrap';
import { Formik } from 'formik';
import * as yup from 'yup';
Validation Schema:
const validationSchema = yup.object({
companyName: yup
.string()
.required('Company Name is required')
.min(3, 'Minimum 3 characters allowed')
.max(100, 'Maximum 100 characters allowed'),
});
Form:
<Formik
validationSchema={validationSchema}
initialValues={{
companyName: '',
}}
onSubmit={() => {}}
>
{({ errors, touched }) => (
<Form autoComplete='off'>
<Form.Group>
<Form.Label>
Company Name <span className='text-danger'>(*)</span>
</Form.Label>
<Form.Control type='text' name='companyName' placeholder='Enter Company Name' />
<Form.Control.Feedback type='invalid'>{errors.companyName}</Form.Control.Feedback>
</Form.Group>
</Form>
)}
</Formik>

Seems like your input fields are not connected to the Formik.
You could use the Field from Formik to wire your inputs to Formik.
import * as React from 'react';
import { Container, Row, Col, Form } from 'react-bootstrap';
import { Formik, Field } from 'formik';
import * as yup from 'yup';
const validationSchema = yup.object({
companyName: yup
.string()
.required('Company Name is required')
.min(3, 'Minimum 3 characters allowed')
.max(100, 'Maximum 100 characters allowed'),
});
export default function App() {
return (
<Formik
validationSchema={validationSchema}
initialValues={{
companyName: '',
}}
onSubmit={() => {}}
>
{({ errors, touched }) => (
<Form autoComplete='off'>
<Form.Group>
<Form.Label>
Company Name <span className='text-danger'>(*)</span>
</Form.Label>
<Field type='text' placeholder='Enter Company Name' name="companyName" />
<Form.Control.Feedback type='invalid'>{errors.companyName}</Form.Control.Feedback>
</Form.Group>
</Form>
)}
</Formik>
);
}

Related

Formik form input not accepting decimal number

Here is my complete edit form. I am type checking with prop types. Also using yup for validation with formik. However my input field is not accepting decimal number. Do i need to add custom validation rules for decimal number? Current form's validation and error feedback working properly for number without decimal format. Any hints how can i solve this ? Thanks in advance !
import React from "react";
import PropTypes from "prop-types";
import { Formik, Form, Field } from "formik";
import { Button, Input, FormGroup, Label, FormFeedback } from "reactstrap";
import * as Yup from "yup";
const projectType = PropTypes.shape({
id: PropTypes.number.isRequired,
actual_design: PropTypes.number.isRequired,
actual_development: PropTypes.number.isRequired,
actual_testing: PropTypes.number.isRequired
});
/**
* Custom form field component to make using Reactstrap and Formik together
* easier and less verbose.
*/
const FormField = ({ label, name, touched, errors }) => (
<FormGroup>
<Label for={name}>{label}</Label>
<Input
type="number"
name={name}
id={name}
tag={Field}
invalid={touched[name] && !!errors[name]}
min={0}
required
/>
{touched[name] && errors[name] && (
<FormFeedback>{errors[name]}</FormFeedback>
)}
</FormGroup>
);
FormField.propTypes = {
label: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
touched: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired
};
/**
* Form for editing the actual hours for a project.
*/
const EditProjectForm = ({ project, onSubmit }) => (
<Formik
initialValues={{
actual_design: projectType.actual_design,
actual_development: projectType.actual_development,
actual_testing: projectType.actual_testing
}}
validationSchema={Yup.object().shape({
actual_design: Yup.number()
.min(0)
.required()
.label("Actual design hours"),
actual_development: Yup.number()
.min(0)
.required()
.label("Actual development hours"),
actual_testing: Yup.number()
.min(0)
.required()
.label("Actual testing hours")
})}
onSubmit={onSubmit}
>
{({ touched, errors, isSubmitting }) => (
<Form>
<FormField
name="actual_design"
type="number"
label="Actual design hours"
touched={touched}
errors={errors}
/>
<FormField
name="actual_development"
label="Actual development hours"
touched={touched}
errors={errors}
/>
<FormField
name="actual_testing"
label="Actual testing hours"
touched={touched}
errors={errors}
/>
<Button type="submit" color="primary" disabled={isSubmitting}>
UPDATE
</Button>
</Form>
)}
</Formik>
);
EditProjectForm.propTypes = {
project: projectType.isRequired,
onSubmit: PropTypes.func.isRequired
};
export default EditProjectForm;
I assume that you see a native browser validation message, because type="number" by default only allows integer with step=1, so you can fix it by adding step="any".
More information you can find there - Is there a float input type in HTML5?

React, MUI + Formik lag on scroll

I'm currently in the process of creating a simple form using MUI TextFields, Formik and some Yup validation.
I ran into some issues with performance with lower spec devices when scrolling and I have extracted stuff like MUI styles and other components such as toastify notifications.
I am relatively new to react, but I have used MUI + Formik + Yup before with a much more complex form without any lag when I scroll and I now can't seem to fix it. The documentation for MUI has also been extremely confusing with many different versions, depracated use cases and so on. I figured useMemo() could work in this instance as there is sure to be some excessive re-rendering, but I am not sure how to implement it.
I'll gladly give more info if needed.
import React from "react";
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import {emailSuccess, emailError, emailSubmitting} from "../lib/toasts";
import {useSubmit} from "../lib/useSubmit";
import {postJson} from "../lib/http";
import {useStyles} from "../lib/inputStyle";
import {TextField} from "#mui/material";
import SendRoundedIcon from "#mui/icons-material/SendRounded";
import { useFormik } from "formik";
import * as Yup from "yup";
export default function Contact () {
const toastId = React.useRef(null);
const classes = useStyles();
const formik = useFormik({
initialValues: {
name: '',
email: '',
message: ''
},
validationSchema: Yup.object({
name: Yup
.string()
.max(255)
.required(
'Name is required'),
email: Yup
.string()
.email(
'Must be a valid email')
.max(255)
.required(
'E-mail address is required'),
message: Yup
.string()
.max(1200)
.required(
'You cannot send an empty message')
}),
onSubmit: () => {
//This should be optional... Please fix!
}
});
const { handleSubmit: handleEmail, submitting, error } = useSubmit(
async () => {
emailSubmitting(toastId);
await postJson("/api/mail", {
name: formik.values.name,
email: formik.values.email,
message: formik.values.message,
});
},
() => {
emailSuccess(toastId);
},
);
if (error) {
emailError(toastId)
}
return (
<div className="contact">
<div className="section">
<div className="contact-innerRef"/>
<h4>Contact</h4>
<div className="contact-card">
<h5>Get In Touch</h5>
<hr/>
<form className="contact-form" action="" method="post" encType="text/plain">
<TextField className={classes.root}
error={Boolean(formik.touched.name && formik.errors.name)}
helperText={formik.touched.name && formik.errors.name}
label="Name"
margin="normal"
name="name"
onChange={formik.handleChange}
value={formik.values.name}
variant="outlined"
/>
<TextField className={classes.root}
error={Boolean(formik.touched.email && formik.errors.email)}
helperText={formik.touched.email && formik.errors.email}
label="Email"
margin="normal"
name="email"
onChange={formik.handleChange}
value={formik.values.email}
variant="outlined"
/>
<TextField className={classes.root}
error={Boolean(formik.touched.message && formik.errors.message)}
helperText={formik.touched.message && formik.errors.message}
label="Message"
margin="normal"
name="message"
onChange={formik.handleChange}
value={formik.values.message}
variant="outlined"
multiline
rows={7}
/>
</form>
<button onClick={handleEmail} disabled={!formik.isValid || !formik.dirty || submitting}
className="btn">
<SendRoundedIcon height="50%"/>
</button>
<ToastContainer />
</div>
</div>
</div>
);
}```

React radio buttons not displaying in browser

I am new to React and formik and struggling to get my radio buttons to display in the browser. I think it is because the radio buttons are not defined as props in my formikcontrol function. How do I add it to the other props in my formikcontrol function? Please advise how to solve this issue. Thanks in advance
App.js:
import React from 'react';
import './App.css';
import FormikContainer from './components/FormikContainer';
import LoginForm from './components/LoginForm';
import Registrationform from './components/RegistrationForm';
function App() {
return (
<div>
<LoginForm />
<Registrationform />
</div>
);
}
export default App;
RegistrationForm:
import React from 'react';
import { Formik, Form } from 'formik';
import * as yup from 'yup';
import FormikControl from './FormikControl';
function Registrationform() {
const options = [
{ key: 'Email', value: 'emailmoc' },
{ key: 'Telephone', vlaue: 'telephonemoc' }
];
const initialValues = {
email: '',
password: '',
confirmPassword: '',
modeOfContact: '',
phone: ''
};
const validationSchema = yup.object({
email: yup.string().email('Invalid email format').required('Required'),
password: yup.string().required('Required'),
confirmPassword: yup
.string()
.oneOf([yup.ref('password'), ''], 'Passwords must match')
.required('required'),
modeOfContact: yup.string().required('Required'),
phone: yup.string().when('modeOfContact', {
is: 'telephonemoc',
then: yup.string().required('Required')
})
});
const onSubmit = (values) => {
console.log('Form data', values);
};
return (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={onSubmit}
>
{(formik) => {
return (
<Form>
<FormikControl
control="input"
type="email"
label="Email"
name="email"
/>
<FormikControl
control="input"
type="password"
label="Password"
name="password"
/>
<FormikControl
control="input"
type="password"
label="Confirm Password"
name="confirmPassword"
/>
<FormikControl
control="radio"
type="Mode of contact"
label="modeOfContact"
options={options}
/>
<FormikControl
control="input"
type="text"
label="Phone number"
name="phone"
/>
<button type="submit" disabled={!formik.isValid}>
Submit
</button>
</Form>
);
}}
</Formik>
);
}
export default Registrationform;
FormikControl:
import React from 'react';
function FormikControl({ control, id, label, ...rest }) {
return (
<>
{control === 'input' && <label htmlFor={id}>{label}</label>}
<input id={id} {...rest} />
</>
);
}
export default FormikControl;
FormikContainer:
import React from 'react';
import { Formik, Form } from 'formik';
import * as Yup from 'yup';
import FormikControl from './FormikControl';
function FormikContainer() {
const initialValues = {
email: '',
password: ''
};
const validationschema = Yup.object({
email: Yup.string().required('Required')
});
const onSubmit = (values) => console.log('Form data', values);
return (
<div>
<Formik
initialValues={initialValues}
validationschema={validationschema}
onSubmit={onSubmit}
>
{(formik) => (
<Form>
<FormikControl
control="input"
type="email"
label="Email"
name="email"
/>
<FormikControl
control="input"
type="password"
label="Password"
name="password"
/>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
</div>
);
}
export default FormikContainer;
In order to get radio button rendered using Formik, you have to import Field component and use it by sending type="radio", here is how it should look like:
enter link description here
Or by using the same component you created but with type="radio" :
<FormikControl
control="input"
type="radio"
.....
/>

Problem with validation: if I click one input it will show error in the another input

I really stuck up. I have a problem with my validation scheme. When I clicking on password field it shows an error also in the new password field. Validation scheme looks like right, but maybe I have missed something. So, this is my form:
And when I focusing field with current password error also is appearing inside the new password field. This is my code:
import React, { useState } from "react";
import { useFormik } from "formik";
import { useSelector } from "react-redux";
import { UserPasswordChangeSchema } from "common/types/user/userPasswordChangeSchema.type";
import { InputText, Button } from "../../components/common/index";
import { passChange } from "helpers/passChange";
import './SecurityPage.css'
import { Redirect } from "react-router-dom";
export const SecurityPage: React.FC = () => {
const email = useSelector((state: any) => state.userprofile.profile ? state.userprofile.profile.email : null)
const [makeRedirect, setMakeRedirect] = useState<boolean>(false);
const formik = useFormik(
{
initialValues: {
oldPassword: '',
password: '',
passwordConfirm: ''
},
onSubmit: async values => {
if (
await passChange({
email,
oldPassword: values.oldPassword,
newPassword: values.passwordConfirm
})
) {
setMakeRedirect(true);
}
},
validationSchema: UserPasswordChangeSchema
}
)
if (makeRedirect) {
return <Redirect to='/' />;
}
console.log(formik.touched)
return (
<div className="security">
<div className="security-info">
<h1 className="security-info-header">Security</h1>
<h2 className="security-info-subheader">Here you can change your password.</h2>
</div>
<form action="" className="security-form" onSubmit={formik.handleSubmit}>
<InputText
name={"Current password"}
propName={"oldPassword"}
value={formik.values.oldPassword}
isPassword={true}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
touched={formik.touched.oldPassword}
errorMsg={formik.errors.oldPassword}
width={451}
/>
<InputText
name={"New password"}
propName={"password"}
value={formik.values.password}
isPassword={true}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
touched={formik.touched.password}
errorMsg={formik.errors.password}
width={451}
/>
<InputText
name={"Confirm new password"}
propName={"passwordConfirm"}
value={formik.values.passwordConfirm}
isPassword={true}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
touched={formik.touched.passwordConfirm}
errorMsg={formik.errors.passwordConfirm}
width={451}
/>
<Button
isSubmit={true}
primary={true}
width={'451px'}
textCenter={true}>
Save
</Button>
</form>
</div>
)
}
And validation scheme:
import * as Yup from 'yup';
const UserPasswordChangeSchema = Yup.object({
oldPassword: Yup.string()
.trim()
.required("Password is required"),
password: Yup.string()
.trim()
.required("New password is required")
.min(8, "The password must contain 8 - 24 characters")
.max(24, "The password must contain 8 - 24 characters"),
passwordConfirm: Yup.string()
.trim()
.oneOf([Yup.ref("password"), null], "Passwords must match")
});
export { UserPasswordChangeSchema }
I wiil be very appreciated for your help
Try to import the Field and ErrorMessage component from Formik
import { Field, ErrorMessage } from 'formik';
and replace the InputText for this new Field something like:
<Field
placeHolder={"Current password"}
type="password"
name="oldPassword"
className="form-control"
value={formik.values.oldPassword}
/>
<ErrorMessage name="oldPassword" />
and repeat for the others inputs but changes the data.

How to set and get a datepicker value using antd with formik?

Here i am creating Datepicker with antd and passing this antd datepicker to formik field.My sample code for Datepicker with antd
import React from "react";
import { Form, DatePicker } from "antd"
import { Field } from "formik";
import moment from 'moment';
const FormItem = Form.Item;
function onChange(date, dateString) {
console.log(date, dateString);
}
const dateFormat = "MM-DD-YYYY"
// Here i am adding antd error message through DateInput
const DateInput = ({
field,
form: { touched, errors },
...props
}) => {
const errorMsg = touched[field.name] && errors[field.name]
const validateStatus = errorMsg ? "error"
: (touched[field.name] && !errors[field.name]) ? "success"
: undefined
return (
<div>
<FormItem
label={props.label}
help={errorMsg}
validateStatus={validateStatus}
hasFeedback
{...props.formitemlayout}>
<DatePicker onChange={onChange} defaultPickerValue={moment()}/>
</FormItem>
</div>
)
}
export default DateInput
i am adding this ant component to formik field component,submit the form using handleSubmit and applying the YUP validations. iam getting a problem was submitting the form iam getting the required validation of DatePicker, and problem is selecting the values of DatePicker iam not getting the value and validation message is displayed after submitting the form.
class FormikApollo extends React.Component {
render() {
const { values, handleSubmit, setFieldValue } = this.props
return (
<div align="center">
<Form onSubmit={handleSubmit}>
<Field
name="username"
label="Name"
placeholder="Enter a Name"
component={TextField}
value={values.username}
formitemlayout={formItemLayout}
/>
<Field
name="email"
label="Email"
placeholder="Enter an Email"
component={TextField}
value={values.email}
formitemlayout={formItemLayout}
/>
<Field
name="password"
label="Password"
type="password"
placeholder="Enter a Password"
component={TextField}
formitemlayout={formItemLayout}
/>
<Field
name="dateofbirth"
label="dateOfBirth"
type="date"
component={DateInput}
formitemlayout={formItemLayout}
defaultValue={values.dateofbirth}
format={dateFormat}
/>
<Button type="primary" htmlType="submit">Submit</Button>
</Form>
)
}
}
Here i am getting the values through withFormik and submitting the form using handleSubmit. Why iam not getting datepicker value and why validation message is displayed after selecting a datepicker value?
const FormikApp = (withFormik)({
mapPropsToValues({ username, email, password, dateofbirth }) {
return {
username: username || '',
email: email || '',
password: password || '',
dateofbirth: dateofbirth || ''
}
},
validationSchema: Yup.object().shape({
username: Yup.string()
.min(3, "Username must be above 3 characters")
.required("Username is required"),
email: Yup.string()
.email("Invalid Email !!")
.required("Email is required"),
password: Yup.string()
.min(6, "Password must be above 6 characters")
.required("Password is required"),
dateofbirth: Yup.string().required("Date is required")
}),
handleSubmit(values, { resetForm }) {
resetForm();
console.log(values)
}
})(FormikApollo)
In your DateInput component try to set value with setFieldValue() method of Formik whether it is valid or not. I believe you can extract it from via: form: { touched, errors, setFieldValue }.
Also check touched items in your form, and make sure that you are changing the value of your date field.

Resources