In ReactJs using Formik library to handle form & Yup library - reactjs

In ReactJs using formik library to handle form & Yup library for form validation rule, as i am trying to use "yup.array().of()" validator to validate array of fields (name, gender) which are passed from other component through props and i have stored this components in another component using accordion which is like the stepper, but it isn't working for me as i am not able to get the validation.
function FormBrowserG(props) {
const { intl } = props;
const peopleArray = props.peopleArrayValue;
//initial values
const initialValues = {
people: []
}
//creating the validation schema
const validationSchema = Yup.object().shape({
people: Yup.array().of(
Yup.object().shape({
name: Yup.string()
.required('Name is required'),
gender: Yup.string()
.required('gender is required')
})
)
});
//using useFormik
const formik = useFormik({
initialValues,
validationSchema,
onSubmit: (values) => {
}
});
const people = []
for (let index = 0; index < props.numberOfPeopleValue; index++) {
people.push(
<fieldset className="fieldset">
<legend className="legend">Person</legend>
<div className="display-flex">
<div className="formname">
<label className="gendername"><FormattedMessage id="AUTH.GENDER.LABEL" /></label>
<select
{...formik.getFieldProps('gender')}
className="form-control">
<option>Select</option>
<option key={10}>Male</option>
<option key={20}>Female </option>
</select>
{formik.touched.gender && formik.errors.gender ? (
<div className="warnings">{formik.errors.gender}</div>
) : null}
</div>
<div className="formo">
<label className="gendername"><FormattedMessage id="AUTH.SIGN_UP.NAME.LABEL" /></label>
<input
type="text"
id={`name${index}`}
className="name"
{...formik.getFieldProps(`name${index}`)}
required
/>
{formik.touched.name && formik.errors.name ? (
<div className="warnings">{formik.errors.name}</div>
) : null}
</div>
</div>
</fieldset>
)
}
return (
<>
<form onSubmit={formik.handleSubmit}>
<div className="row">
{people}
</div>
</form>
<div className="bofferingbtn">
<Link to={{
pathname: "/Confirmation",
state: value
}}>
<Button variant="contained" type="submit" className="obtn" size="large" color="primary" >
<FormattedMessage id="AUTH.OFFERING.BUTTON" />
</Button>
</Link>
</div>
</>
)
}
export default injectIntl(FormBrowserG)

You need to pass your values to formik like this -
initialValues={{ name: "", gender: "" }}
onSubmit={...}
//this will automatically validate the field
validationSchema={validationSchema}

Related

Conditional validation in Yup and formik and semantic-ui

I am new to Formik & Yup Validation. I have some fields depend on semantic-ui dropdown .When I click on a field a particular input field and a drop down will open. I cannot do the validation properly. I want when Facebook is selected these fields show validate not Instagram fields, when Instagram is selected only these fields show validation not Facebook fields.. here is the working sandbox link https://codesandbox.io/s/adoring-davinci-z4qlnw?file=/src/UserInfo.jsx My validation code is here..
export const ProfessionalValidation = yup.object().shape({
category: yup.array().required('Category Name is required').nullable(),
FBfollowers: yup.string().required('Atleast One Flatform is required').nullable(),
Instafollowers: yup.string().required('Atleast One Flatform is required').nullable(),
facebookLink: yup.string().required('This Field is required').nullable(),
platform: yup.array().required('Atleast One Flatform is required').nullable(),
})
userinfo is here
import { ProfessionalValidation } from "./Validation";
import { ErrorMessage, Formik, Form, Field } from "formik";
import {
ageGroup,
categoryOptions,
Platform,
gender,
followers,
subscribers
} from "./Data";
import { Dropdown } from "semantic-ui-react";
import "./style.css";
import "./styles.css";
const UserInfo = () => {
const [selectedOptions, setOptions] = useState([]);
const [savedata, setSaveData] = useState([]);
const defaultValue = {
category: "",
platform: "",
facebookLink: "",
InstaLink: "",
Instafollowers: "",
FBfollowers: "",
subscribers: "",
youtube: ""
};
return (
<>
<Formik
initialValues={defaultValue}
validationSchema={ProfessionalValidation}
onSubmit={handleSubmit}
>
{({ values, setFieldValue }) => (
<Form>
<section className="form-styling my-4">
<div className="container">
<div className="input-box2">
<div>
<label htmlFor="category">Category*</label>
<Field
clearable
name={`category`}
fluid
multiple
search
selection
options={categoryOptions}
component={Dropdown}
placeholder="Select Category"
onChange={(_, { value }) =>
setFieldValue("category", value)
}
/>
<span style={{ color: "red" }}>
<ErrorMessage name="category" />
</span>
</div>
<div>
<label htmlFor="platform">Platform*</label>
<Field
clearable
name={`platform`}
fluid
multiple
search
selection
options={Platform}
placeholder="Select Platform"
component={Dropdown}
onChange={(_, { value }) => {
const onDropChange = () => {
setOptions(value);
setSaveData(selectedOptions);
setFieldValue("platform", value);
};
onDropChange();
}}
//onChange= {onDropChange}
/>
<span style={{ color: "red" }}>
<ErrorMessage name="platform" />
</span>
<span className="mt-3 d-block">Select at least 1</span>
</div>
</div>
{/* all selected platform show in this div */}
<div className="platform-box">
<div>
{selectedOptions.find((word) => word === "Facebook") &&
values.platform.find((word) => word === "Facebook") ? (
<div>
<div className="platform-details">
<div>
<h5>Facebook</h5>
</div>
<div>
<Field
type="text"
name="facebookLink"
placeholder="Account Link"
//onBlur={(value) => setFieldValue("facebookLink",value.currentTarget)}
/>
<span className="text-danger">
<ErrorMessage name="facebookLink" />
</span>
</div>
<div>
<Field
className="label ui selection fluid dropdown"
clearable
name={`FBfollowers`}
fluid
search
selection
options={followers}
component={Dropdown}
placeholder="Select followers"
onChange={(_, { value }) =>
setFieldValue("FBfollowers", value)
}
/>
<span className="text-danger">
<ErrorMessage name="FBfollowers" />
</span>
</div>
</div>
<span>Verify the Followers in 2-3 Business Days</span>
</div>
) : null}
{selectedOptions.find((word) => word === "Instagram") ? (
<div>
<div className="platform-details">
<div>
<h5>Instagram</h5>
</div>
<div>
<Field
type="text"
name="InstaLink"
placeholder="Account Link"
/>
</div>
<div>
<Field
className="label ui selection fluid dropdown"
clearable
name={`Instafollowers`}
fluid
search
selection
options={followers}
component={Dropdown}
placeholder="Select followers"
onChange={(_, { value }) =>
setFieldValue("Instafollowers", value)
}
/>
<span style={{ color: "red" }}>
<ErrorMessage name="Instafollowers" />
</span>
</div>
</div>
<span>Verify the Followers in 2-3 Business Days</span>
</div>
) : null}
</div>
</div>
</div>
{/* container end */}
</section>
{/* additional info */}
<section className="additional-Info ">
<div className="container">
<div className="main-box"></div>
<div className="text-start my-5">
<button type="submit" className="border-0">
<span className="btn-form btn btn-info">Continue</span>
</button>
</div>
</div>
</section>
</Form>
)}
</Formik>
</>
);
};
export default UserInfo;
Yup provides .when api for mixed validation.
You can use it like below.
FBfollowers: yup
.string()
.when("platform", {
is: (val) => val?.includes("Facebook"),
then: yup.string().required("Atleast One Flatform is required"),
otherwise: yup.string()
})
.nullable(),
Since the value for platform field is an array, I've used .includes to check whether user has selected specific platform. If this returns true then the field will be made mandatory, otherwise it will be non-mandatory.
Here is updated code for validation.jsx file.
import * as yup from "yup";
export const ProfessionalValidation = yup.object().shape({
category: yup.array().required("Category Name is required").nullable(),
FBfollowers: yup
.string()
.when("platform", {
is: (val) => val?.includes("Facebook"),
then: yup.string().required("Atleast One Flatform is required"),
otherwise: yup.string()
})
.nullable(),
Instafollowers: yup
.string()
.when("platform", {
is: (val) => val?.includes("Instagram"),
then: yup.string().required("Atleast One Flatform is required"),
otherwise: yup.string()
})
.nullable(),
facebookLink: yup
.string()
.when("platform", {
is: (val) => val?.includes("Facebook"),
then: yup.string().required("This field is required"),
otherwise: yup.string()
})
.nullable(),
platform: yup.array().min(1, "Atleast one Field is required").nullable()
});
And here is the updated working code sandbox.
Here is more on the .when api of Yup.
Hope this helps.

How to validate a field that may not exist or does exist?

I need to validate the field with yup. However, that field is custom and may not exist on the page.
Example: https://codesandbox.io/s/nervous-banach-mcuo6r?file=/src/App.js
const validationSchema = yup.object().shape({
field: yup.string().required()
});
export default function App() {
const [inputVisible, setInputVisible] = useState(false);
return (
<Formik
validationSchema={validationSchema}
onSubmit={() => {}}
initialValues={{ field: "" }}
>
<div className="App">
{inputVisible && <input name="field" placeholder="Type..." />}
<button onClick={() => setInputVisible(!inputVisible)}>
Show input
</button>
</div>
</Formik>
);
}

How to get simultaneously data from 2 forms in react-hook-form from different components

I have have 2 inputs from different components and need to get data from them.
Moreover, place should be default, different in both components ('Place1', 'Place2').
The result should be written in to objects:
[{name: 'Tom', surname: 'Smith', place: 'Place1'},
{name: 'John', surname: 'Johnes', place: 'Place2'}]
const { register, handleSubmit } = useForm({
mode: 'onChange',
defaultValues: {
name: '',
surname: '',
place: '',
},
});
const saveAdress = (data) => {
console.log(data);
};
<form onSubmit={handleSubmit(saveAdress)}>
<div >
<GetName1 register={register} />
<GetName2 register={register} />
</div>
<button type="submit">
Save
</button>
</form>
And components:
const getName1 = () => {
return (
<form>
<input placeholder="name" {...register(0.name'}>
<input placeholder="surname" {...register(0.surname'}>
</form>
)
}
const getName2 = () => {
return (
<form>
<input placeholder="name" {...register(1.name'}>
<input placeholder="surname" {...register(1.surname'}>
</form>
)
}
So, the user fills any forms (null, one or both), press the button and the data should be saved like an array of objects.
You can use useFieldArray from react-hook-form.
Here's an example mentioned in the official documentation.
import React from "react";
import { useForm, useFieldArray } from "react-hook-form";
function App() {
const { register, control, handleSubmit, reset, trigger, setError } = useForm({
// defaultValues: {}; you can populate the fields by this attribute
});
const { fields, append } = useFieldArray({
control,
name: "test"
});
return (
<form onSubmit={handleSubmit(data => console.log(data))}>
<ul>
{fields.map((item, index) => (
<li key={item.id}>
<input {...register(`test.${index}.firstName`)} />
<Controller
render={({ field }) => <input {...field} />}
name={`test.${index}.lastName`}
control={control}
/>
<button type="button" onClick={() => remove(index)}>Delete</button>
</li>
))}
</ul>
<button
type="button"
onClick={() => append({ firstName: "bill", lastName: "luo" })}
>
append
</button>
<input type="submit" />
</form>
);
}
Here's a working codesandbox link.
If it's possible for you then kindly share the codesandbox link of your working example. We can implement this in your current example.

Yup required validation not working and not giving error for empty field

I'm working on a form using formik and yup. I have added required schema, but it is not working. I can easily save having empty input fields. I have tried and googled but nothing worked.
I want to make it mandatory and it should give error if field is empty.
snippet of yup schema validation
opening_time: Yup.string().required("Opening time is Requried"),
closing_time: Yup.string().required("Closing time is Requried"),
address: Yup.string().required("Address is Requried"),
about: Yup.string().required("About is Required"),
Input field snippet
<div class="form-group mb-0">
<label>
About<span className="text-danger">*</span>
</label>
<textarea
name="about"
onChange={formik.handleChange}
value={formik.values.about}
class="form-control"
rows="5"
required
/>
{formik.touched.about && formik.errors.about ? (
<div className="err">
{formik.errors.about}
{console.log(formik.errors.about)}
</div>
) : null}
</div>
Try the following:
import React from 'react';
import { Formik, Form, Field } from 'formik';
import * as Yup from 'yup';
const SignupSchema = Yup.object().shape({
opening_time: Yup.string().required("Opening time is Requried"),
closing_time: Yup.string().required("Closing time is Requried"),
address: Yup.string().required("Address is Requried"),
about: Yup.string().required("About is Required"),
});
function ValidationSchemaExample() {
function updateDoctorProfile(e, values) {
console.log(`e: ${e}`);
console.log(`values: ${values}`)
}
return (
<div>
<h1>Signup</h1>
<Formik
initialValues={{
opening_time: "",
closing_time: "",
address: "",
about: "",
}}
validationSchema={SignupSchema}
onSubmit={values => {
// same shape as initial values
console.log(values);
}}
>
{({ values, errors, touched, handleChange, handleSubmit, isSubmitting }) => (
< div className="form-group mb-0">
<label>
About<span className="text-danger">*</span>
</label>
<textarea
name="about"
onChange={handleChange}
value={values.about}
className="form-control"
required
/>
<button type="submit" onClick={(e) => {
handleSubmit();
updateDoctorProfile(e, values);
}} disabled={isSubmitting}>
Submit
</button>
{touched.about && errors.about ? (
<div className="err">
{errors.about}
{console.log(errors.about)}
</div>
) : null}
</div>
)}
</Formik>
</div >
);
}
export default ValidationSchemaExample;
The only change is that the button tag's onClick attribute is passed the handleSubmit function along with your updateProfile function.

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