I have a form that i'm controlling by formik, when i fill all the fields and press the buttom submit, the function onSubmit is called and my form have this values reseted.
Sometimes my data is incorrect (like a duplicate email) and i need to persist this data.
How i can do this?
This is my code:
const schema = Yup.object().shape({
login: Yup.string()
.email('Email não possui formato válido')
.required('Informe o email!'),
password: Yup.string().required('Informe a senha!'),
})
const formik = useFormik({
initialValues: {
login: '', password: '', inactive: false
},
validationSchema: schema,
onSubmit: values => {
registerUser(values)
}
})
return (
<form onSubmit={formik.handleSubmit} className={classes.form} noValidate>
<Grid container spacing={3}>
<Grid item xs={12}>
<Typography className={classes.observation} component="h6">* Necessário preenchimento do cadastro geral para liberar permissão de telas</Typography>
</Grid>
<Grid item xs={5}>
<TextField
value={formik.values.login}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={formik.touched.login ? formik.errors.login : ""}
error={formik.touched.login && Boolean(formik.errors.login)}
variant="outlined"
margin="normal"
required
fullWidth
id="email"
label="E-mail"
name="login"
autoComplete="email"
/>
</Grid>
<Grid item xs={5}>
<TextField
value={formik.values.password}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={formik.touched.password ? formik.errors.password : ""}
error={formik.touched.password && Boolean(formik.errors.password)}
variant="outlined"
margin="normal"
required
fullWidth
type="password"
id="password"
label="Senha"
name="password"
/>
</Grid>
<Grid item xs={2}>
<FormControlLabel
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.inactive}
control={<Switch color="primary" />}
label="Inativo"
labelPlacement="top"
/>
</Grid>
<Grid item xs={3}>
<Button fullWidth
variant="contained"
color="primary"
type="submit"
>
Cadastrar
</Button>
</Grid>
</Grid>
</form>
);
Use the Form from formik, and the default is to not reset on submit:
import { Formik, Form } from "formik";
function DemoComp(){
return(
<Formik
initialValues={{ fieldOneVal: "" }}
onSubmit={async (formsData, {setSubmitting, resetForm}) => {
setSubmitting(true)
// async request
// --> if wanted to reset on submit: resetForm();
console.log(formsData)
setSubmitting(false)
}}
>
{({ values, isSubmitting, handleChange, handleBlur, handleSubmit }) => (
<Form>
<input
type="text"
name="fieldOneVal"
value={values.fieldOneVal}
onChange={handleChange}
onBlur={handleBlur}
/>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
)
}
You need to update the following line:
<form onSubmit={(e) => { e.preventDefault(); formik.handleSubmit(e)}} className={classes.form} noValidate>
You need to define the function to be async and as async response handling is not mention in your code so it's hard to give exact solution
<Formik
initialValues={{ fieldOneVal: formvalues.fieldOneVal }}
onSubmit={async (values, { resetForm }) => {
// setFormvalues(({fieldOneVal:values.fieldOneval}) don't know why you're manipulating state
const resp=await someAsyncFunc()
if (resp){
reset the state to initial values}
else{
pop up modal display with errors}
}}
>
I faced the same problem formik resetting the form before i get a response from the server.This solution helped me.
import { Formik, Form } from "formik";
function DemoComp(){
const [formvalues,setFormvalues]= useState({
{ fieldOneVal: "" }
)
return(
<Formik
initialValues={{ fieldOneVal: formvalues.fieldOneVal }}
onSubmit={(values, { resetForm }) => {
setFormvalues(({fieldOneVal:values.fieldOneval})
// async request
if sucess{
reset the state to initial values}
else{
pop up modal display with errors}
}}
>
{({ values, isSubmitting, handleChange, handleBlur, handleSubmit }) => (
<Form>
<input
type="text"
name="fieldOneVal"
value={values.fieldOneVal}
onChange={handleChange}
onBlur={handleBlur}
/>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
)
}
Related
I am trying to prepopulate a Formik with JSON data. The data appears fine and it is returned. I have two issues.
I am using ASP.NET Core as the back end. Should I use values or initialValues as the variable? When I use initialValues, I get assign fields. The labels are still populated.
The labels are still populated, after I send a default value. All the fields are read only and I cannot edit them:
Here is my code:
function OrdersToReporting() {
const [initialValues, setInitialValues] = useState<OrdersToReportingViewModels>({ });
useEffect(() => {
agent.Settings.getOrdersSettings().then((userList) => {
let solutionName = (new URLSearchParams(window.location.search)).get("SolutionName");
let solutionWebsite = (new URLSearchParams(window.location.search)).get("SolutionWebsite");
userList.data.SolutionName = solutionName;
userList.data.SolutionWebsite = solutionWebsite;
setInitialValues(userList.data);
});
}, []);
const handleSubmit = async (
values: any,
{ resetForm, setErrors, setStatus, setSubmitting }: any
) => {
try {
await timeOut(1500);
agent.Settings.saveOrdersSettings(values);
resetForm();
setStatus({ sent: true });
setSubmitting(false);
} catch (error: any) {
setStatus({ sent: false });
setErrors({ submit: error.message });
setSubmitting(false);
}
};
return (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{({
errors,
handleBlur,
handleChange,
handleSubmit,
isSubmitting,
touched,
values,
status,
}) => (
{isSubmitting ? (
<Box display="flex" justifyContent="center" my={6}>
<CircularProgress />
</Box>
) : (
<form onSubmit={handleSubmit}>
<Grid container spacing={6}>
<Grid item md={6}>
<TextField
name="SolutionName"
label="Solution Name"
value={initialValues.SolutionName}
fullWidth
onBlur={handleBlur}
onChange={handleChange}
variant="outlined"
my={2}
/>
<TextField
name="SolutionWebsite"
label="Solution Website"
value={initialValues.SolutionWebsite}
fullWidth
onBlur={handleBlur}
onChange={handleChange}
variant="outlined"
my={2}
/>
<TextField
name="lastDownloadUtc"
label="Last Download Date Start"
value={initialValues.LastDownloadUtc}
fullWidth
onBlur={handleBlur}
onChange={handleChange}
variant="outlined"
my={2}
/>
</Grid>
<Grid item md={6}>
<TextField
name="lastDownloadUtcEnd"
label="Last Download Date End"
value={initialValues.LastDownloadUtcEnd}
fullWidth
onBlur={handleBlur}
onChange={handleChange}
variant="outlined"
my={2}
/>
</Grid>
</Grid>
<Button
type="submit"
variant="contained"
color="primary"
mt={3}
>
Save changes
</Button>
</form>
)}
)}
</Formik>
);
}
For your questions,
You should use initialValues to assign default values to your fields.
since you are getting the data from some function, there might be a delay in setting initialValues. So pass enableReinitialize={true} in formik props.
You were not able to change the values because you set
value={initialValues.fieldName} // always value will be this unless you change initialValues state
for every textfield. Always this will be same even if you edit.
Just remove the value property formik will handle it by default.
I have an authorization and validation form for registration. I need to to make my form available for click events at my button. At first i added the resetform function in order to notice how my form actually works but after each click nothing happens.
import { Button, TextField } from "#mui/material";
import * as yup from "yup";
import { Formik } from "formik";
export const RegistrationForm = () => {
const validationSchema = yup.object().shape({
name: yup
.string()
.typeError("Должно быть строкой")
.required("Обязательно")
.matches(/^[аА-яЯ\s]+$/, "Имя должно состоять из русских букв"),
secondName: yup
.string()
.typeError("Должно быть строкой")
.required("Обязательно")
.matches(/^[аА-яЯ\s]+$/, "Фамилия должна состоять из русских букв"),
login: yup
.string()
.typeError("Должно быть строкой")
.required("Обязательно"),
password: yup
.string()
.typeError("Должно быть строкой")
.required("Обязательно"),
confirmPassword: yup
.string()
.oneOf([yup.ref("password")], "Пароли не совпадают")
.required("Обязательно"),
email: yup.string().email("Введите верный email").required("Обязательно"),
confirmEmail: yup
.string()
.email("Введите верный email")
.oneOf([yup.ref("email")], "Email не совпадают")
.required("Обязательно"),
});
return (
<>
<header>
<div className="head-btns">
<Button variant="outlined">Зарегистрироваться</Button>
<Button variant="outlined">Авторизоваться</Button>
</div>
</header>
<Formik
initialValues={{
name: "",
secondName: "",
login: "",
password: "",
confirmPassword: "",
email: "",
confirmEmail: "",
}}
validateOnBlur
onSubmit={({ resetForm }) => resetForm()}
validationSchema={validationSchema}
>
{({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isValid,
dirty,
}) => (
<div className="register">
<TextField
id="outlined-basic"
label="Имя"
variant="outlined"
className="field"
name={"name"}
onChange={handleChange}
onBlur={handleBlur}
value={values.name}
/>
{touched.name && errors.name && (
<p className={"error"}>{errors.name}</p>
)}
<TextField
id="outlined-basic"
label="Фамилия"
variant="outlined"
className="field"
name={"secondName"}
onChange={handleChange}
onBlur={handleBlur}
value={values.secondName}
/>
{touched.secondName && errors.secondName && (
<p className={"error"}>{errors.secondName}</p>
)}
<TextField
id="outlined-basic"
label="Логин"
variant="outlined"
className="field"
name={"login"}
onChange={handleChange}
onBlur={handleBlur}
value={values.login}
/>
{touched.login && errors.login && (
<p className={"error"}>{errors.login}</p>
)}
<TextField
type="password"
id="outlined-basic"
label="Пароль"
variant="outlined"
className="field"
name={"password"}
onChange={handleChange}
onBlur={handleBlur}
value={values.password}
/>
{touched.password && errors.password && (
<p className={"error"}>{errors.password}</p>
)}
<TextField
type="password"
id="outlined-basic"
label="Подтвердите пароль"
variant="outlined"
className="field"
name={"confirmPassword"}
onChange={handleChange}
onBlur={handleBlur}
value={values.confirmPassword}
/>
{touched.confirmPassword && errors.confirmPassword && (
<p className={"error"}>{errors.confirmPassword}</p>
)}
<TextField
id="outlined-basic"
label="Email"
variant="outlined"
className="field"
name={"email"}
onChange={handleChange}
onBlur={handleBlur}
value={values.email}
/>
{touched.email && errors.email && (
<p className={"error"}>{errors.email}</p>
)}
<TextField
id="outlined-basic"
label="Подтвердите Email"
variant="outlined"
className="field"
name={"confirmEmail"}
onChange={handleChange}
onBlur={handleBlur}
value={values.confirmEmail}
/>
{touched.confirmEmail && errors.confirmEmail && (
<p className={"error"}>{errors.confirmEmail}</p>
)}
<Button
onClick={handleSubmit}
disabled={!isValid || !dirty}
type="submit"
variant="contained"
className="btn"
>
Зарегистрироваться
</Button>
</div>
)}
</Formik>
</>
);
};
I clicked the button and nothing happens but it should reset all inputs after this action
The onSubmit prop given to the <Formik /> component should be like this:
<Formik
onSubmit={(values, { resetForm }) => resetForm()}
validationSchema={validationSchema}
>
</Formik>
Formik options are passed to the second parameter of the submit handler.
And you don't need to explicitly give your Button onClick handler with type="submit" if you define your form in the HTML form tag.
<Formik
onSubmit={(values, { resetForm }) => resetForm()}
validationSchema={validationSchema}
>
{({ handleSubmit }) => (
<form onSubmit={handleSubmit>
{/* your form */}
<Button
type="submit" // giving type submit to a button enclosed in a form will automatically call handleSubmit method
>
Submit
</Button>
</form>
)}
</Formik>
I've created a register form I tried to validate the form onBlur and the one has an email check with backend Apis, I have an issue when I validate any other that case mail check API render every time
I tried with onBlur and onChange validation false that case working fine, But I need onBlur validation, Any Ideas
Thanks for Help
const CompanyRegister = (props) => {
const classes = useStyles();
var [mailError, setMailError] = useState(false);
const initialValues = {
name: "",
website: "",
addressLine1: "",
city: "",
state: "",
mail: "",
};
const validationSchema = yup.object({
name: yup.string().strict().trim().required("Enter the ComapnyName"),
website: yup
.string()
.matches(
/((https?):\/\/)?(www.)?[a-z0-9]+(\.[a-z]{2,}){1,3}(#?\/?[a-zA-Z0-9#]+)*\/?(\?[a-zA-Z0-9-_]+=[a-zA-Z0-9-%]+&?)?$/,
"Enter correct url!"
)
.strict()
.trim()
.required("Enter the Website"),
pincode: yup.number("Invalid pincode").positive("Invalid pincode"),
phone: yup.number("InValid Phone Number"),
altPhone: yup.number("InValid Phone Number"),
mail: yup
.string()
.email("Enter correct Format")
.strict()
.trim()
.required("Enter the Correct Email"),
});
const validCheck = (data) => {
var mailData = { mail: data };
axios.post(baseurl + "companymailcheck", mailData).then((res) => {
if (res.data == "Invalid") {
setMailError(true);
} else {
setMailError(false);
}
});
};
const onSubmit = (data) => {
axios
.post(baseurl + "companyregisteration", data)
.then((res) => {
history.push(`/login`);
toast.success("Successfully Created");
})
.catch((err) => {
toast.error("Some Thing Error");
});
};
return (
<>
<div className={classes.back}>
<Link to="/">Home</Link>
</div>
<Container component="main" className={classes.layout}>
<div className={classes.paper}>
<Typography component="h1" variant="h5">
Register
</Typography>
<Formik
initialValues={initialValues}
onSubmit={onSubmit}
validateOnChange={false}
validateOnBlur={true}
validationSchema={validationSchema}
>
{({ values, isValid }) => {
return (
<Form autoComplete="off">
<Grid container spacing={4}>
<Grid item xs={12} sm={6}>
<Field
as={TextField}
name="name"
fullWidth
label="Company Name"
autoFocus
required
/>
<ErrorMessage name="name">
{(error) => (
<div className={classes.error}>{error}</div>
)}
</ErrorMessage>
</Grid>
<Grid item xs={12} sm={6}>
<Field
as={TextField}
name="website"
fullWidth
label="Website"
required
/>
<ErrorMessage name="website">
{(error) => (
<div className={classes.error}>{error}</div>
)}
</ErrorMessage>
</Grid>
<Grid item xs={12} sm={12}>
<Field
as={TextField}
name="addressLine1"
type="text"
fullWidth
label="Address Line1"
/>
</Grid>
<Grid item xs={12} sm={6}>
<Field
as={TextField}
name="mail"
fullWidth
label="Mail"
required
validate={() => validCheck(values.mail)}
/>
<ErrorMessage name="mail">
{(error) => (
<div className={classes.error}>{error}</div>
)}
</ErrorMessage>
{mailError ? (
<span style={{ color: "red" }} id="errormessage">
Mail Already There!
</span>
) : null}
</Grid>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
disabled={!isValid || mailError}
>
Register
</Button>
</Grid>
</Form>
);
}}
</Formik>
</div>
</Container>
</>
);
};
export default CompanyRegister;
As long as it's fine to perform the validation during form submission then I would extend the onSubmit and put the check there.
You can use the second parameter which will return the FormikBag containing all relevant setter's to invalidate the form-field properly. Reference: https://formik.org/docs/api/formik#onsubmit-values-values-formikbag-formikbag--void--promiseany
In this case I would use setFieldError(field, message). Reference: https://formik.org/docs/api/formik#setfielderror-field-string-errormsg-string--void
<Formik initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={(values, {setFieldError}) => {
return validateMail(values.mail)
.then(() => {
// all good - continue with your submit handler
return onSubmit(values)
})
.catch(error => {
// something wrong? validate the error response + validation message OR set it statically.
setFieldError('mail', 'Mail Already There!')
})
}}
>
I have a form in my react component with handleSubmit. What I need to do is when I submit the form(on save click) the save button automatically should get disabled and when I get the response it automatically gets enabled.
handleSubmit = async({ company, email }, { setSubmitting, setErrors }) => {
setSubmitting(true)
const { value: { status, message } } = await this.props.createCompany({ name: company, email })
if (status) {
this.fetchCompanies()
this.closeModal()
} else {
setErrors({ email: message })
}
}
<Formik
initialValues={loginDetails}
validationSchema={loginSchema}
onSubmit={(values, formikProps) => this.handleSubmit(values, formikProps)}
>
{({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting
}) => (
<form onSubmit={handleSubmit}>
<div className="col-md-12">
<div className="form-group material-textfield">
<input type="text" name="email" value={values.email} onChange={handleChange} className="form-control material-textfield-input"/>
<ErrorMessage component="span" name="email" className="invalid-feedback d-block"/>
<label className="material-textfield-label">Email<span>*</span></label>
</div>
</div>
<button type="submit" className="btn btn-dark btn-lg w-100" disabled={isSubmitting}>Save</button>
</form>
)}
</Formik>
And for this I have used setSubmitting function from formik. But it doesn't work.
Kindly help.
This should help:
const onSubmitHandler = (values, formik) => {
persistYourData(values).then(r => {
formik.setSubmitting(false);
}).catch(error => console.log(error));
}
...
<Button variant="contained" color={"primary"} onClick={pr.handleSubmit} disabled={ ((!(pr.isValid && pr.dirty)) || pr.isSubmitting) }>Submit</Button>
#You can disable the button with **formik.isSubmitting** or **formik.errors** #
<Formik
initialValues={{
email: '',
password: '',
}}
validationSchema={Yup.object({
email: Yup.string().email('Invalid email address').required('Required'),
password: Yup.string().required('Required'),
})}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
});
}}
>
{(formik) => (
<Form>
<h1>Login</h1>
<Field type='email' name='email' />
<br />
<ErrorMessage name='email' />
<br />
<Field type='password' name='password' />
<br />
<ErrorMessage name='password' />
<br />
<button disabled={formik.isSubmitting || formik.errors}>Login</button>
</Form>
)}
</Formik>
I have the following Formik form
<Formik
initialValues={{
email: '',
password: ''
}}
onSubmit={(values, { setSubmitting }) => {
console.log(JSON.stringify(values));
setSubmitting(true);
fetch('/login', {
method: 'POST',
body: JSON.stringify(values),
headers: {
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.then(response => {
console.log(response);
setSubmitting(false);
if (!response.success) return showAlert();
login(response.user);
history.push('/');
})
.catch(console.log);
}}
>
{({ isSubmitting }) => (
<Form className={classes.form}>
<TextField
required
variant="outlined"
margin="normal"
fullWidth
label="Email"
name="email"
autoFocus
/>
<TextField
required
variant="outlined"
margin="normal"
fullWidth
name="password"
label="Wachtwoord"
type="password"
/>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
disabled={isSubmitting}
>
Log in
</Button>
<Grid container>
<Grid item xs>
<Link href="/register" variant="body2">
{'Nog geen account? Registreer nu!'}
</Link>
</Grid>
</Grid>
</Form>
)}
</Formik>
For some reason the values in the onSubmit are empty, how can I set it to the values inside the form? The only difference between this form and another one I got to work is that this one does not have a validation schema but I can not imagine that is required
You should take a look at the example in formik's docs.
TextField isn't connect to Formik. When the value of TextField changes, Formik don't change, you need Field (import { Field } from 'formik') that will render TextField.
Example for email
<Field
name="email"
render={({ field /* _form */ }) => (
<TextField
required
variant="outlined"
margin="normal"
fullWidth
label="Email"
autoFocus
{...field}
/>
)}
/>
If you are using a external input, you should use this way.
You need to pass this
onChangeText={handleChange('email')}
onBlur={handleBlur('email')}
value={values.email}
to the Textinput, and for Password aswell.
Your Textinput doesn't track any changes or submission.
Check here