i am trying to create a general input like this
const TextInput = ({
name,
register = () => {},
errors,
serverErrors,
...props
}) => {
return (
<div >
<input
type="text"
{...register(name, {
pattern: { value: /^[A-Za-z]+$/i, message: "Invalid Input" },
})}
{...props}
/>
{errors?.[name] && (
<span className="text-errorColor">{errors?.[name]?.message}</span>
)}
</div>
);
};
I will use this input in form and use Yup to validate this form
const schema = yup
.object({
first_name: yup
.string()
.required("This field is required")
.max(20, "max 20 characters"),
})
.required();
const SignupForm = ({ signUpContact, signUpType }) => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(schema),
});
return (
<form onSubmit={handleSubmit(onSubmit)}>
<TextInput
name="first_name"
register={register}
errors={errors}
serverErrors={error}
placeholder="First Name"
/>
</form>
);
};
but the problem is that the validations in TextInput Competent aren't running
i think i can't use Register Validation with Yup validation.
as you see I won't duplicate validation [A-Za-z] every time I use TextInput, is there any way to do this?
For input just register the field using react-hook-form
<input
type="text"
{...register(name)}
{...props}
/>
Use yup to handle all the validation logic
yup
.string()
.trim()
.matches(/^[A-Za-z]+$/i , 'Invalid Input')
.max(20, "max 20 characters"),
.required();
Related
https://codesandbox.io/s/nice-cohen-k3kdtq?file=/src/App.js Here is the codesandbox example of my code
What I need to do is when I click the 'Preview' button I want to disable validation on last two fields (price, category) and when I click 'Submit' I want to validate all the fields. I tried to change react-hook-form resolver depending on state but it doesn't let me and had an idea about making fields not required when boolean variable from component changes but I don't know how can I send this variable to yup schema
const nftSchema = yup.object().shape({
NFTCollectionAddress: yup
.string()
.required("Collection address is required")
.test("len", "Not a valid address", (val) => val.length === 42)
.matches("0x", "Not a valid address"),
NFTTokenID: yup
.number()
.typeError("You must specify a number")
.required("Token ID is required"),
price: yup
.string()
.required("Price is required")
.test("inputEntry", "The field should have digits only", digitsOnly)
.test(
"maxDigitsAfterDecimal",
"Number cannot have more than 18 digits after decimal",
(number) => /^\d+(\.\d{1,18})?$/.test(number)
),
category: yup.string().required("Category is required")
});
export default function App() {
const {
register,
handleSubmit,
formState: { errors }
} = useForm({
resolver: yupResolver(nftSchema),
});
const onSubmit = (data) => {
};
const handlePreview = (data) => {
};
return (
<form>
<h4>Token ID</h4>
<input
name="NFTTokenID"
type="text"
{...register("NFTTokenID")}
/>
<h4>Collection</h4>
<input
name="NFTCollectionAddress"
type="text"
{...register("NFTCollectionAddress")}
/>
<h4>Price</h4>
<input
name="price"
type="text"
{...register("price")}
/>
<h4>Category</h4>
<input
name="category"
type="text"
{...register("category")}
/>
<button onClick={handleSubmit(onSubmit)}>Submit</button>
<button onClick={handleSubmit(handlePreview)}>Preview</button>
</form>
</div>
);
}
What about creating a different schema for preview, and changing the schema passed to yupResolver based on isPreview? On Preview button also would just set the isPreview state, not use the handleSubmit function.
I have a value in a form (email) that I want to transform into lowercase. I have a transform in Yup that's working, but Formik does not show a lowercase value.
How do I make it so that, when I enter the email in uppercase, it gets converted to lowercase automatically?
Here is my code:
import React from "react";
import { render } from "react-dom";
import { Formik } from "formik";
import * as Yup from "yup";
import { DisplayFormikState } from "./helper";
import "./style.css";
const validationSchema = Yup.object({
email: Yup.string()
.transform(function (value, originalvalue) {
return this.isType(value) && value !== null ? value.toLowerCase() : value;
})
.email()
.required()
.label("Email")
});
const App = () => (
<div className="app">
<Formik
initialValues={{ name: "" }}
onSubmit={() => {}}
validationSchema={validationSchema}
>
{(props) => {
const { handleBlur, handleChange, values, errors, touched } = props;
return (
<form onSubmit={props.handleSubmit}>
<h1>Email Form</h1>
<input
type="email"
name="email"
onChange={handleChange}
onBlur={handleBlur}
value={values.email}
/>
{errors.email && touched.email && errors.email}
<button type="submit">Submit</button>
<DisplayFormikState {...props} />
</form>
);
}}
</Formik>
</div>
);
render(<App />, document.getElementById("root"));
In your form, Formik and Yup essentially have two separate duties:
Formik manages the form's state (values)
Yup performs validations and tells Formik whether the values are valid or not
Yup will not set the values in your form.
Remove the case conversion from the Yup transformation. Instead, simply set the value to lowercase and pass it to setFieldValue() before yup does the validation.
const validationSchema = Yup.object({
email: Yup.string()
.email()
.required()
.label("Email")
});
const App = () => (
<div className="app">
<Formik
initialValues={{ name: "" }}
onSubmit={() => {}}
validationSchema={validationSchema}
>
{(props) => {
const { handleBlur, setFieldValue, values, errors, touched } = props;
return (
<form onSubmit={props.handleSubmit}>
<h1>Email Form</h1>
<input
type="email"
name="email"
onChange={(e)=>{
const value = e.target.value || "";
setFieldValue('email', value.toLowerCase());
}}
onBlur={handleBlur}
value={values.email}
/>
{errors.email && touched.email && errors.email}
<button type="submit">Submit</button>
<DisplayFormikState {...props} />
</form>
);
}}
</Formik>
</div>
);
render(<App />, document.getElementById("root"));
I am using Formik and Yup validation for my form which has firstname ,lastname & username. Username should be without spaces so I am using a onChange event and value. Yup validation is working for firstname and Lastname but not for username. When logging my values found out that username is not getting updated. I am a newbie please help me out. Thanks in advance.
const CustomTextInput =({label, ...props}) =>{
const [field, meta] = useField(props);
return(
<>
<label className="required" htmlFor={props.id || props.name}>{label}</label>
{meta.touched && meta.error ? (
<div className="error">{meta.error}</div>
):null}
<input className="text-input" {...field} {...props}/>
</>
)
}
function App(){
const [regnum, Setreg]= useState("");
function avoid(e){
Setreg(e.target.value.replace(/\s+/g,''));
}
return(
<Styles>
<Formik
initialValues={{
firstname:'',
lastname: '',
username:'',
phone1:'',
email:''
}}
validationSchema={
Yup.object({
firstname: Yup.string()
.required('Required'),
lastname: Yup.string()
.required('Required'),
username: Yup.string()
.min(4,"Username should be greater than 4 characters")
.max(15,"Wooah! Username cannot be that big")
.required('Required'),
})
}
onSubmit= {(values, { setSubmitting , resetForm }) => {
setTimeout(()=> {
//My api call here
resetForm()
setSubmitting(false);
},2000)
}
}>
{props => (
<Form>
<CustomTextInput label="First Name" name="firstname" type="text" placeholder="first Name"/>
<CustomTextInput label="Last Name" name="lastname" type="text" placeholder="Last Name"/>
<CustomTextInput label="UserName" name="username" type="text" value={regnum} onChange={(event)=>avoid(event)} placeholder="Spaces will be removed"/>
<div>
<button type="submit" >{props.isSubmitting ? "Loading..." : "Submit"}</button>
</div>
</Form>
</Formik>
</Styles>
);
}
export default App;
The question is, do you want to prevent the user from using a username with spaces? If so, the easiest way is to do it through yup.
validationSchema={
Yup.object({
firstname: Yup.string()
.required('Required'),
lastname: Yup.string()
.required('Required'),
username: Yup.string()
.required('Required').matches("/\s/", "Cannot contain spaces"),
})
}
Otherwise, if you allow the user to write the username with spaces, but you want it for whichever reasons without spaces, you have to manipulate the data in the submitHandler.
By giving it a custom onChange handler to username, you overrode formik since formik uses it's onChange under the hood. Manipulate the data in the submitHandler, prior to submitting, let formik do it's thing.
I using material-ui with react.
I want to do validations such as require and maxlength and minlength. according to material-ui docs I have to use the error prop and helperText to display the error. which mean I have to trigger a function myself that check the control and display the error. :(
I wounder if this is the right way to handle validation with react, the Textfield itself can't display require message (for example)? I have to specify each error myself? for example in angular I just add require or minlength and the control display the correct error.
Or maybe there is an easy way to do it?
got it :) here my ex :
import { Link } from 'react-router-dom';
import useForm from "react-hook-form";
import * as yup from 'yup';
const LoginFormSchema = yup.object().shape({
password: yup.string().required().min(4),
username: yup.string().required().min(4)
});
export function LoginForm(props) {
const { register, handleSubmit, watch, errors } = useForm({ defaultValues, validationSchema: LoginFormSchema });
const onSubmit = data => { props.onSubmit(data); }
<div className="form-container">
<form className="form" onSubmit={handleSubmit(onSubmit)}>
<div className="form-header">
<i className="material-icons">
account_circle
</i>
<h2>Login Form</h2>
</div>
<TextField name="username" label="username" inputRef={register} />
<span className="error-message">
{errors.username && errors.username.type === "required" && "username is required"}
{errors.username && errors.username.type === "min" && "username required to be more than 4 characters"}
</span>
<TextField type="password" name="password" label="password" inputRef={register} />
<span className="error-message">
{errors.password && errors.password.type === "required" && "password is required"}
{errors.password && errors.password.type === "min" && "password required to be more than 4 characters"}
</span>
</form>
You need to install yup and formik: npm i -s yup formik
Here is a working sample with formik yup and material-ui:
import React from "react";
import { Formik, Form, useField } from "formik";
import { TextField } from "#material-ui/core";
import * as yup from "yup";
//Reusable Textbox
const MyTextField = ({
placeholder,
type = "normal",
...props
}) => {
const [field, meta] = useField<{}>(props);
const errorText = meta.error && meta.touched ? meta.error : "";
return (
<TextField
variant="outlined"
margin="normal"
type={type}
placeholder={placeholder}
{...field}
helperText={errorText}
error={!!errorText}
/>
);
};
const validationSchema = yup.object({
username: yup
.string()
.required()
.max(30)
.min(2)
.label("Username"),
password: yup
.string()
.required()
.max(30)
.min(2)
.label("Password")
});
const Signin = ({ history }) => {
return (
<div className="SignupOuter">
<Formik
validateOnChange={true}
initialValues={{
username: "",
password: "",
loading: false
}}
validationSchema={validationSchema}
onSubmit={async (data1, { setSubmitting }) => {
setSubmitting(true);
//Call API here
}}
>
{({ values, errors, isSubmitting }) => (
<Form className="Signup">
<MyTextField placeholder="Username" name="username" />
<MyTextField
placeholder="Password"
name="password"
type="password"
/>
</Form>
)}
</Formik>
</div>
);
};
export default Signin;
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.