Edit
As it turns out, it was working all along -- the issue was because my handleLogin method was async
New sandbox:
I have a basic Form component. It passes setSubmitting as one of the available methods, and it passes isSubmitting as well. I want to disable the submit button while the form is submitting, but I'm having trouble with this.
Initially, I had a <form> element and I was trying to set setSubmitting(true) in the below part:
<form
onSubmit={(credentials) => {
setSubmitting(true); // <--
handleSubmit(credentials);
}}
>
But this didn't work. So I've tried getting rid of the <form> and changing <Button> to type="button" instead of submit, and I did,
<Button
color="primary"
disabled={isSubmitting}
fullWidth
size="large"
type="button"
variant="contained"
onClick={() => {
setSubmitting(true);
handleLogin(values);
}}
>
Submit
</Button>
But the problem with this, is that in order to do setSubmitting(false) in case of an error is that I have to do this,
onClick={() => {
setSubmitting(true);
handleLogin(values, setSubmitting); // <--
}}
And in addition to this, I have no use for onSubmit={handleLogin}, but if I remove that, Typescript complains.
There's got to be an easier way to accomplish this (without using useFormik).
What can I do here?
Here is the component:
import * as React from "react";
import { Formik } from "formik";
import { Box, Button, TextField } from "#material-ui/core";
const Form = React.memo(() => {
const handleLogin = React.useCallback(async (credentials, setSubmitting) => {
console.log(credentials);
setTimeout(() => {
setSubmitting(false);
}, 2000);
}, []);
return (
<Formik
initialValues={{
email: ""
}}
onSubmit={handleLogin} // removing this line make Typescript complain
>
{({
handleSubmit,
handleChange,
setSubmitting,
isSubmitting,
values
}) => (
<div>
<TextField
fullWidth
label="Email"
margin="normal"
name="email"
onChange={handleChange}
value={values.email}
variant="outlined"
/>
<Box sx={{ my: 2 }}>
<Button
color="primary"
disabled={isSubmitting}
fullWidth
size="large"
type="button"
variant="contained"
onClick={() => {
setSubmitting(true);
handleLogin(values, setSubmitting);
}}
>
Submit
</Button>
</Box>
</div>
)}
</Formik>
);
});
export default Form;
You forget to put the form inside your Formik component
<Formik>
{...}
<form onSubmit={handleSubmit}>
{...}
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</form>
</Formik>
so now you can use your button as submit.
demo: https://stackblitz.com/edit/react-egp1gc?file=src%2FForm.js
Related
I have ran into this bug, where I press the Login button, but nothing happens. In browser it works perfectly, but has some issues with capacitorJS. I have tried adding onClick function right on the button, removing Formik functionality, placing something before the try - catch. However, when using with Formik, validation actually works, it tells that the inputs can't be empty and checks email format.
This is the UI part
<form autoComplete="on" onSubmit={handleSubmit}>
<BrandTextField
name={'email'}
onChange={handleChange}
value={values.email}
placeholder={t('common:emailPlaceholder.exampleEmail')}
type={'email'}
label={t('common:email')}
className='mb-3'
error={Boolean(touched.email) && Boolean(errors.email)}
fullWidth
/>
<div>
<BrandTextField
name={'password'}
type={'password'}
onChange={handleChange}
value={values.password}
placeholder={t('common:password')}
label={t('common:password')}
className='mb-3'
error={Boolean(touched.password) && Boolean(errors.password)}
fullWidth
/>
<div
className="login-forgot-password"
onClick={() => history.push('/forgot-password')}
>
{t('signIn.signInScreen.forgotPassword')}
</div>
</div>
<div className="login-submit">
<Button type="submit" size={'auto'}>
{t('common:signIn')}
</Button>
</div>
</form>
And here is the useFormik
const {
values,
handleChange,
errors,
touched,
handleSubmit,
} = useFormik<CredentialsPayload>({
initialValues,
validationSchema,
onSubmit: async (values, { setErrors }) => {
toast.error('submit');
try {
await dispatch(actions.signIn(values));
} catch (err) {
if (err.message) {
toast.error(err.message);
}
setErrors(err.errors ?? {});
}
},
});
The issue was with the way I handled submit, It should be written onClick of button
I am using Formik and have the following setup below where I want to be able to reset the form when the user presses the "Cancel" button. On return to the form, all form values should be reset to initialValues which are all nulls.
<Formik
enableReinitialize
initialValues={{
...INITIAL_FORM_STATE
}}
validationSchema={ FORM_VALIDATION }
onSubmit={handleSubmit}
>
{({ values, errors, isSubmitting, isValid, setFieldValue, handleChange, resetForm }) => (
<Form>
.....
I have the following code for the Cancel button:
<Button
text="Cancel"
startIcon={<UndoIcon />}
variant="contained"
color="default"
className={classes.buttons}
component={Link}
to={'/home'}
onClick={() => {
{resetForm}
setMenu("Home")
}}
/>
After entering some text into a form field and pressing the Cancel button, which directs me back to the Home page, I then go back to the form and notice that my text is still in state within the form and not resetting.
Can anyone please assist with what I am missing.
<Button
text="Cancel"
startIcon={<UndoIcon />}
variant="contained"
color="default"
className={classes.buttons}
component={Link}
to={'/home'}
onClick={() => {
resetForm()
setMenu("Home")
}}
/>
You should use the resetForm() as a function call
you just need to set values of the input boxes to formik values:
<Formik
enableReinitialize
initialValues={{
...INITIAL_FORM_STATE
}}
validationSchema={ FORM_VALIDATION }
onSubmit={handleSubmit}
>
{({ values, errors, isSubmitting, isValid, setFieldValue, handleChange, resetForm }) => (
<input value={values.propertyName}/>
<Form>
and now resetForm should work well
You must change values through values since you dont have access to resetForm from the button.
<Button
text="Cancel"
startIcon={<UndoIcon />}
variant="contained"
color="default"
className={classes.buttons}
component={Link}
to={'/home'}
onClick={() => {
values = {
someProperty: null,
}
}}
/>
As as I see your are using Material UI. I notice that you have a "to" property in your Button component I think you have to decide either remaining on the same page and reset your form or redirecting to another page. If you want to remain on the same page I would suggest you to get rid of it because this causes some conflict. You can implement it like this:
return (<Formik
enableReinitialize
initialValues={{
...INITIAL_FORM_STATE
}}
validationSchema={ FORM_VALIDATION }
onSubmit={(values, actions) => {
actions.setSubmitting(false);
console.log("Submit form", values);
}}
>
{({ values, errors, isSubmitting, isValid, setFieldValue, handleChange, handleSubmit, resetForm }) => (
<Form onSubmit={handleSubmit}>
..... some inputs
<Button
text="Cancel"
startIcon={<UndoIcon />}
variant="contained"
color="default"
className={classes.buttons}
component={Link}
onClick={() => handleReset(resetForm)}
/>
</Form>
)}
</Formik>
);
And inside you class create a handleReset method:
const handleReset = (resetForm) => {
if (window.confirm('Reset?')) {
resetForm();
setMenu("Home");
}
};
`
const myForm = useFormik({
initialValues:{
value1:'',
value2:''
},
onSubmit:( values ) = >{
//submit data
........
//reset form after submit
myForm.resetForm()
}
)
on return
<form onSubmit={myForm.submit}>
........
<Button type="submit"/>
<Button onClick={()=>myForm.resetForm()}>Reset</Button>
</form>
`
I am building a login form with Formik with Materail UI, but Formik doesn't recognize Button of Material UI. If I replace the Button with html button everything works. Could anybody explain why it's not working with the Button component. Below is my code:
import React, { ReactElement } from "react";
import TextField from "#material-ui/core/TextField";
import FormControl from "#material-ui/core/FormControl";
import { Formik, Form } from "formik";
import { makeStyles } from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
import Box from "#material-ui/core/Box";
const useStyles = makeStyles((theme) => ({
root: {
margin: theme.spacing(1),
width: 200,
display: "flex",
},
input: {
marginTop: 5,
marginBottom: 5,
},
}));
interface Props {}
export default function loginForm({}: Props): ReactElement {
const classes = useStyles();
return (
<Box className={classes.root}>
<Formik
initialValues={{ username: "", password: "" }}
validate={(values) => {
const errors: { username?: string; password?: string } = {};
if (!values.username) {
errors.username = "Required";
} else if (!values.password) {
errors.password = "Required";
}
return errors;
}}
onSubmit={(values, { setSubmitting }) => {
console.log("values: ", values);
setSubmitting(false);
}}
>
{({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting,
}) => (
<form onSubmit={handleSubmit}>
<FormControl>
<TextField
className={classes.input}
error={errors.username ? true : false}
type="username"
name="username"
onChange={handleChange}
onBlur={handleBlur}
value={values.username}
label="Username"
variant="outlined"
helperText={
errors.username && touched.username && errors.username
}
/>
<TextField
className={classes.input}
error={errors.password ? true : false}
type="password"
name="password"
onChange={handleChange}
onBlur={handleBlur}
value={values.password}
label="Password"
variant="outlined"
helperText={
errors.password && touched.password && errors.password
}
/>
</FormControl>
<Box display="flex" justifyContent="space-between">
<Button
variant="contained"
color="primary"
disabled={isSubmitting}
>
Submit
</Button>
<Button
variant="contained"
color="secondary"
disabled={isSubmitting}
>
Cancel
</Button>
</Box>
</form>
)}
</Formik>
</Box>
);
}
please note the code above doesn't work, but if you replace Button with button, the form works.
In most browsers, a HTML button by default has the type=submit which means that Formik's submit handler will be called. A Material-UI button does not have this default so the submit handler will never be called. Try adding type=submit to your <Button> props.
(Also, check out Formik's Material-UI integration examples)
You should add the id to the form.
<form onSubmit={handleSubmit} id="myForm">
And bind the form id with submit button
<Button
type="submit"
form="myForm"
>
In my application i try to get a value from a form tag.
This is the component where i want to output the value:
import React, { useState } from "react";
import { Form, Input, Button, Space } from "antd";
import { PlusOutlined } from "#ant-design/icons";
const Inner = props => {
const [formVal, setFormVal] = useState([]);
const [defaultMode, setDefaultMode] = useState(true);
const onFinish = values => {
setFormVal(values);
console.log("Received values of form:", values);
};
const setFieldOnEdit = index => () => {
console.log("index", index);
};
return (
<Form.List onFinish={onFinish} name={[props.fieldKey, "inner"]}>
{(fields, { add, remove }) => {
return (
<div>
{fields.map((field, index) => (
<Space
key={field.key}
style={{ display: "flex", marginBottom: 8 }}
align="start"
>
<Form.Item
{...field}
name={[field.name, "first"]}
fieldKey={[field.fieldKey, "first"]}
rules={[{ required: true, message: "Missing first name" }]}
>
<Input placeholder="First Name" />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit inner{setFieldOnEdit(index)}
</Button>
</Form.Item>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
>
<PlusOutlined /> Add field to inner
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
);
};
export default Inner;
Now the submit button from form works ok when i click on:
<Button type="primary" htmlType="submit">
Submit inner{setFieldOnEdit(index)}
</Button>
But i want, when i click on this button, to trigger and setFieldOnEdit(index) function, and to get inside:
const setFieldOnEdit = index => () => {
console.log("index", index); //here i want to get the index when i click on the button
};
... the index. But when i click on the button, i just submit the form, but not trigger the setFieldOnEdit() function. How to trigger the function and to get the index inside the above function?demo: https://codesandbox.io/s/aged-architecture-0r2si?file=/InnerForm.js
You can call setFieldOnEdit(index) with the Buttons' onClick property:
<Button type="primary" htmlType="submit" onClick={setFieldOnEdit(index)}>
Submit inner
</Button>
I am trying to use async with onSubmit with following code for Formik in React
import React from "react";
import { Formik, Form, Field } from "formik";
import { Row, Col, Button } from "react-bootstrap";
const AddUser = () => {
const initialValues = {
name: "",
};
return (
<>
<Row className="h-100">
<Col xs={12} sm={1}></Col>
<Col xs={12} sm={10} className="align-self-center">
<div className="block-header px-3 py-2">Add Dataset</div>
<div className="dashboard-block dashboard-dark">
<Formik
initialValues={initialValues}
onSubmit={async (values, { setSubmitting }) => {
alert("hi");
setSubmitting(false);
}}
>
{({ isValid, submitForm, isSubmitting, values }) => {
return (
<Form>
<Field
name="name"
label="Name"
placeholder="Dataset Name"
/>
<Button
type="submit"
disabled={!isValid || isSubmitting}
className="w-100 btn btn-success"
onClick={submitForm}
>
Add Dataset
</Button>
</Form>
);
}}
</Formik>
</div>
</Col>
<Col xs={12} sm={1}></Col>
</Row>
</>
);
};
export default AddUser;
When I try to submit. It does alert 'hi' twice. When I don't use onSubmit as async then it works fine.
What am I doing wrong or is there any other way to perform async functionalities as I need to perform RestAPI calls?
Delete type="submit", because there is already an action onClick={submitForm}
<Button
type="submit"
disabled={!isValid || isSubmitting}
className="w-100 btn btn-success"
onClick={submitForm}
>
Be sure to NOT add both
onClick={formik.formik.handleSubmit}
and
<form onSubmit={formik.handleSubmit}>.
Should be one or the other.
I faced the same issue. Adding e.preventDefault() in my Form Submit Handler worked
for me.
onSubmitHandler = (e) => {
e.preventDefault();
//Handle submission
}