Formik ReactBoostrap Form not validating - reactjs

I have formik form build on the react-boostrap form. I have a custom onchange for handling the inputs. The issue is that validation error throw even if there is a value in the form field. Also, If some value is typed then error message is not gone. I guess the validation is not working at all. Please help me on this.
This is the code for react-bootstrap form https://react-bootstrap.github.io/components/forms/#forms-validation-libraries
import React from "react";
import * as yup from "yup";
export default function StepByStepForm() {
const [myForm, setMyForm] = useState({});
const handleInput = (e) => {
const value = e.target.value;
const name = e.target.name;
setMyForm((prev) => ({
...prev,
[name]: value,
}));
};
const form1schema = yup.object({
company_name: yup.string().required(),
});
function Step1Form() {
return (
<Formik validationSchema={form1schema}>
{({
touched,
isValid,
isInvalid,
errors,
handleBlur,
handleChange,
values,
validateForm,
}) => (
<Form noValidate className="formstep1">
<Form.Group controlId="addCompany">
<Form.Label>Company Name*</Form.Label>
<Form.Control
name="company_name"
type="text"
value={myForm.company_name}
onChange={handleInput} // have my custom handling
isInvalid={!!errors.company_name}
/>
<Form.Control.Feedback type="invalid">
{errors.company_name}
</Form.Control.Feedback>
</Form.Group>
<div className="step-progress-btn">
<Button variant="primary" onClick={() => validateForm()}>
Validate
</Button>
</div>
</Form>
)}
</Formik>
);
}
return <div>Step1Form()</div>;
}

Your custom onChange={handleInput} function never passes the value to Formik.
Formik internally keeps track of your form values, so you don't need to add it using the useState method like you are now (const [myForm, setMyForm] = useState({});).
When you add a custom onChange to your form, you change your component state, while Formik's state never updates, so Yup does not have a value to validate.
If you add this just below your closing </Form> tag, you will see Formik never reads your form's updated values:
<pre>
{JSON.stringify(
{
touched,
isValid,
isInvalid,
errors,
handleBlur,
handleChange,
values,
validateForm
},
null,
2
)}
</pre>

The issue was that Formik-Yup needed the onChange={handleChange} to validate
If you have custom or additional functionality on top of handleChange then you need to add the eventlistener on top of handlechange
import React from "react";
import * as yup from "yup";
export default function StepByStepForm() {
const [myForm, setMyForm] = useState({});
const handleInput = (e) => {
const value = e.target.value;
const name = e.target.name;
setMyForm((prev) => ({
...prev,
[name]: value,
}));
};
const form1schema = yup.object({
company_name: yup.string().required(),
});
function Step1Form() {
return (
<Formik validationSchema={form1schema}>
{({
touched,
isValid,
isInvalid,
errors,
handleBlur,
handleChange,
values,
validateForm,
}) => (
<Form noValidate className="formstep1">
<Form.Group controlId="addCompany">
<Form.Label>Company Name*</Form.Label>
<Form.Control
name="company_name"
type="text"
value={myForm.company_name}
onChange={(e) => {
handleChange(e);
handleInput(e);
}}
isInvalid={!!errors.company_name}
/>
<Form.Control.Feedback type="invalid">
{errors.company_name}
</Form.Control.Feedback>
</Form.Group>
<div className="step-progress-btn">
<Button variant="primary" onClick={() => validateForm()}>
Validate
</Button>
</div>
</Form>
)}
</Formik>
);
}
return <div>Step1Form()</div>;
}

Related

React Bootstrap Form.Check with Formik

How can I properly bind Form.Check to a boolean variable using yup and Formik?
React-Bootstrap 4.5 provides an example of using Formik + yup with form inputs. I was able to setup text inputs and selects, but encountered a problem with Form.Check element. I expect it to provide simple boolean value on change, but instead I'm getting an empty array [] or ["on"] when checkbox is checked.
The documentation also has this issue, in the example from the link above form displays this error message:
terms must be a boolean type, but the final value was: ["on"].
My code:
const schema = yup.object({
deactivated: yup.boolean(),
});
const initialValues = {
deactivated: false,
};
return (
<Formik
validationSchema={schema}
onSubmit={(
values
) => {
save(
values.deactivated,
);
}}
initialValues={initialValues}>
{({
handleSubmit,
handleChange,
values,
errors,
}) => (
<Form noValidate onSubmit={handleSubmit}>
<Form.Group controlId="deactivated">
<Form.Check
label="Deactivated"
type="checkbox"
value={values.deactivated}
onChange={handleChange}
isInvalid={!!errors.deactivated}
/>
</Form.Group>
<Button type="submit">Save</Button>
</Form>
)}
</Formik>
);
I was able to handle checkbox changes manually using setFieldValue method:
extract setFieldValue method from Formik
bind checkbox to checked property instead of value
use custom onChange handler: {e => setFieldValue('deactivated', e.target.checked)}
Code:
return (
<Formik
validationSchema={schema}
onSubmit={(
values
) => {
save(
values.deactivated,
);
}}
initialValues={initialValues}>
{({
handleSubmit,
handleChange,
values,
errors,
setFieldValue,
}) => (
<Form noValidate onSubmit={handleSubmit}>
<Form.Group controlId="deactivated">
<Form.Check
label="Deactivated"
type="checkbox"
checked={values.deactivated}
onChange={e => setFieldValue('deactivated', e.target.checked)}
isInvalid={!!errors.deactivated}
/>
</Form.Group>
<<Button type="submit">Save</Button>
</Form>
)}
</Formik>
);

React-bootstrap switch not displaying

I'm trying to incorporate Formik and React-bootstrap.
It's going as expected except the switch component.
Here is my function for rendering the switch:
function ReactSwitch(props){
return(
<>
<Col md={props.col}>
<Form.Group>
<Form.Check
type="switch"
id={props.id}
name={props.name}
label={props.label}
checked={props.checked}
onChange={props.onChange}
/>
</Form.Group>
</Col>
</>
);
}
And here is my initialization for Formik:
const formik = useFormik({
initialValues: {
wflow_switch: false
},
onSubmit: values => {
alert(JSON.stringify(values, null, 2));
},
});
Note that when I change the type from switch to checkbox, it displays a checkbox but still no label. What am I doing wrong? I'm still learning React so any comments are appreciated.
I guess you'll need to use state and enable enablereinitialize
Try this:
export default function FormSwitch() {
// add checked state
const [checked, setChecked] = useState(false)
const { handleSubmit, values } = useFormik({
initialValues: {
// initial value is set 'false' by state
switch: checked
},
// Control whether Formik should reset the form if initialValues changes
enableReinitialize: true,
onSubmit: (values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2))
setSubmitting(false)
}, 400)
}
})
return (
<form className="form" onSubmit={handleSubmit}>
<ReactSwitch
name="switch"
label="Switch"
id="switch"
checked={checked}
onChange={() => setChecked(!checked)}
/>
<button type="submit">
Submit
</button>
</form>
)
}
Edit: Different approach using useField with <Formik>
import { Formik, useField } from "formik"
import { Col, Form } from "react-bootstrap"
const ReactSwitch = ({ ...props }) => {
const [field, form] = useField(props)
return (
<Col md={props.col}>
<Form.Group>
<Form.Check
type="switch"
id={props.id}
checked={field.value.checked}
onChange={() => form.setFieldValue(props.name, !field.value)}
{...field}
{...props}
/>
</Form.Group>
</Col>
)
}
export default function Switch() {
return (
<Formik
initialValues={{
switch: false
}}
onSubmit={values => alert(JSON.stringify(values, null, 2))}
>
{formik => (
<form onSubmit={formik.handleSubmit}>
<ReactSwitch label="Switch" name="switch" id="switch" />
<button type="submit">submit</button>
</form>
)}
</Formik>
)
}

handleSubmit function not successfully firing using Formik

I am using Formik + Yup to construct a form but can't seem to get passed a basic issue, I feel like I'm missing something obvious. My handleSubmit function wont fire on button click - even something simple like a console log. If I add a pure function to the onClick button handler it fires, but Formik doesn't seem to be passing down my handleSubmit constructed with the withFormik HOC.
I've tried adding the handler to the Form component, doesn't work there either.
const formikEnhancer = withFormik({
mapPropsToValues: props => ({
firstName: props.firstName || '',
...
}),
validationSchema: yup.object().shape({
firstName: yup.string().required('Please enter your first name'),
...
}),
handleSubmit: (values, formikBag) => {
console.log('test');
}
},
});
const BusinessFundingForm = ({
values,
isSubmitting,
errors,
handleBlur,
handleChange,
handleSubmit,
touched,
data,
}) => {
return (
<Form className="form" id="form-id">
<Row>
<Col xs={12} sm={6}>
<InputField
id="first-name"
type="text"
name="firstName"
value={values.firstName}
onChange={handleChange}
onBlur={handleBlur}
placeholder="First Name"
label="First Name"
/>
{errors.firstName &&
touched.firstName && <Error>{errors.firstName}</Error>}
</Col>
...
</Row>
<Row>
<ButtonWrapper>
<Button
type="submit"
tall
onClick={handleSubmit}
varianttype={
isSubmitting ||
(!!errors.firstName ||
formHelpers.isEmpty(values.firstName))
? 'disabled'
: 'outline'
}
disabled={
isSubmitting ||
(!!errors.firstName ||
formHelpers.isEmpty(values.firstName))
}
text="Submit →"
/>
</ButtonWrapper>
</Row>
</Form>
</FormGrid>
</Element>
);
};
export default formikEnhancer(BusinessFundingForm);
For brevity's sake I deleted all the properties except the firstName property
Sometimes there are errors on the page which prevent Formik from triggering handleSubmit thus its good to check if there are no errors by outputting errors on the screen or console.
To overcome these type of scenarios during development you can always use:
<pre>{JSON.stringify(errors, null, 2)}</pre> as a DOM node so that you are constantly aware of the issue on the UI and then remove it or comment it during committing to your branch.
Also I trust there are no syntax errors on your code because your handleSubmit seems to have extra closing brace.

understanding Formik and React

This is probably not the best place to post this question but where, then?
The code below is taken from Formik's overview page and I'm very confused about the onSubmit handlers:
The form element has an onSubmit property that refers to handleSubmit which is passed on that anonymous function : <form onSubmit={handleSubmit}>. Where does that come from?
The Formik component has an onSubmit property as well:
onSubmit={(values, { setSubmitting }) => { ... }
How do these relate to each other? What is going on?
import React from 'react';
import { Formik } from 'formik';
const Basic = () => (
<div>
<h1>Anywhere in your app!</h1>
<Formik
initialValues={{ email: '', password: '' }}
validate={values => {
let errors = {};
if (!values.email) {
errors.email = 'Required';
} else if (
!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
) {
errors.email = 'Invalid email address';
}
return errors;
}}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
{({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting,
/* and other goodies */
}) => (
<form onSubmit={handleSubmit}>
<input
type="email"
name="email"
onChange={handleChange}
onBlur={handleBlur}
value={values.email}
/>
{errors.email && touched.email && errors.email}
<input
type="password"
name="password"
onChange={handleChange}
onBlur={handleBlur}
value={values.password}
/>
{errors.password && touched.password && errors.password}
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</form>
)}
</Formik>
</div>
);
export default Basic;
The component takes onSubmit as a prop where you can execute code you want to perform when you submit your form. This prop is also given some arguments such as values (values of the form) for you to use in your onSubmit function.
The handleSubmit form is auto generated from the Formik library that automates some common form logics explained here. The handleSubmit will automatically execute onSubmit function mentioned above as part of its phases (pre-submit, validation, submission). Hope that answers your question!

react-boostrap-typeahead reset with formik

I am using the AsyncTypeahead from the react-boostrap-typeahead library along with Formik. Both great little libraries.
A simplified version of my code looks like this
const SearchFilter = withFormik({
mapPropsToValues: (props) {
office: someIncomingValue || [{name: 'office1', id: 1}]
}
})(TheForm)
const TheForm = (props) => {
const {values, handleReset} = props;
return (
<form>
<AsyncTypeahead
defaultSelected={[values.office]}
options={...}
onChange={...SetNewValue...}
onSearch={...}/>
<button onClick={handleReset}
</form>
)
}
By using defaultSelected property on the AsynchTypeahead. i can set a default INITIAL value. But the issue i am having is when i click the button to handleRest, formik does its thing and reset the value back to office 1, but AsynchTypeahead doesnt have a way of manually passing a value back into it. So it does not change. I saw there is a selected prop available, but it just blows up when i try to use it.
Any input would be gerat
UPDATE:
Yes Selected is what i needed. i had to add an onInputChange property to keep the parent in sync with what was being typed.
I had the same question.
I came up with this solution:
import { Formik } from "formik";
import * as Yup from "yup";
import { Typeahead } from 'react-bootstrap-typeahead';
const AddCheckSchema = Yup.object().shape({
title: Yup.string()
.min(2, 'Too Short!')
.max(50, 'Too Long!')
.required('Required')
});
...
render() {
const users = [
{ id: 1, fullname: 'User #1' },
{ id: 2, fullname: 'User #2' },
{ id: 3, fullname: 'User #3' },
];
return (
<Formik
initialValues={{
title: '',
amount: 0,
actualDate: new Date()
}}
validationSchema={AddCheckSchema}
onSubmit={ this.onSubmit}
>
{({
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
setFieldValue,
setFieldTouched,
}) => (
<form onSubmit={handleSubmit}>
<div className="form-group required">
<label className="control-label">Title:</label>
<Typeahead
multiple={false}
onChange={(selected) => {
const value = (selected.length > 0) ? selected[0].fullname : '';
setFieldValue('title', value);
}}
onInputChange={(text, event) => setFieldValue('title', text)}}
onBlur={(e) => setFieldTouched('title', true)}
labelKey="fullname"
options={users}
/>
<div className="text-danger">{touched.title && errors.title}</div>
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary btn-lg">Add check</button>
</div>
</form>
)}
</Formik>
)
}
Of course, you can extract this complex logic (around Typeahead field) into separate component.
Reference to API.
In my case I had to opt for a simpler solution by taking a look at this example here https://ericgio.github.io/react-bootstrap-typeahead/#basic-example
export const SelectSearch = ({name, value, schema, setFieldValue, errors}) => {
const [singleSelections, setSingleSelections] = useState([]);
useEffect(() => {
if (singleSelections.length > 0) {
setFieldValue(name, singleSelections[0].value)
}
}, [singleSelections])
return (
<LabeledField name={name} schema={schema}>
<Typeahead
id="basic-typeahead-single"
multiple={false}
labelKey="label"
options={schema.choices}
onChange={setSingleSelections}
isInvalid={!!errors[name]}
placeholder="Choose a state..."
selected={singleSelections}
/>
<Form.Control.Feedback type="invalid">
{errors[name]}
</Form.Control.Feedback>
</LabeledField>
)
}
This component can then be rendered in the formik context like below
const Component = () => {
return (
<Formik
initialValues={...}
validationSchema={AddCheckSchema}
onSubmit={this.onSubmit}
>
{({
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
setFieldValue,
setFieldTouched,
}) => (
<form onSubmit={handleSubmit}>
<div className="form-group required">
<label className="control-label">Title:</label>
<SelectSearch
name={"inputName"}
choices={choices}
setFieldValue={setFieldValue}
errors={errors}
/>
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary btn-lg">Submit</button>
</div>
</form>
)}
</Formik>
)
}

Resources