Async/Await with Formik? - javascript

I am using formik and I am wondering how to use async/await with onSubmit
<Formik
initialValues={{ email: '' }}
onSubmit={(values, { setSubmitting }) => {
// how to do async/await here.
}}
validationSchema={Yup.object().shape({
email: Yup.string()
.email()
.required('Required'),
})}
>
{props => {
const {
values,
touched,
errors,
dirty,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
handleReset,
} = props;
return (
<form onSubmit={handleSubmit}>
<label htmlFor="email" style={{ display: 'block' }}>
Email
</label>
<input
id="email"
placeholder="Enter your email"
type="text"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
className={
errors.email && touched.email ? 'text-input error' : 'text-input'
}
/>
{errors.email &&
touched.email && <div className="input-feedback">{errors.email}</div>}
<button
type="button"
className="outline"
onClick={handleReset}
disabled={!dirty || isSubmitting}
>
Reset
</button>
<button type="submit" disabled={isSubmitting}>
Submit
</button>
<DisplayFormikState {...props} />
</form>
);
}}
</Formik>

Is something like this what you're looking for?
onSubmit={ async (values, { setSubmitting }) => {
await ...
setSubmitting(false)
}}

As alternative, you could make an seperate function as well.
async function _handleSubmit(values) {
await...
}
onSubmit={(values, { setSubmitting }) => {
_handleSubmit(values)
setSubmitting(false)
}}

Related

Formik with patch api call in Reactjs

i haven't work with rest api and formik so need guidance regarding that.
i have EDIT form where i have one user against id , i want to set user details on form and submit it with updated values and without any error if it kept unchanged.
for this i have to set values of Text Fields somehow and call patch api on submit
this is my form code :
<form onSubmit={handleSubmit}
>
<label className="userlabels">Username</label>
<TextField
name="userName"
placeholder="User Name"
type="text"
id="userName"
onBlur={handleBlur}
value={values.userName}
onChange={handleChange}
error={touched.userName && Boolean(errors.userName)}
helperText={touched.userName && errors.userName}
/>
<label className="userlabels">Fullname</label>
<TextField
name="fullName"
placeholder="Full Name"
type="text"
id="fullName"
onBlur={handleBlur}
value={values.fullName}
onChange={handleChange}
error={touched.fullName && Boolean(errors.fullName)}
helperText={touched.fullName && errors.fullName}
/>
<label className="userlabels">Email</label>
<TextField
name="email"
placeholder="Email"
type="email"
id="email"
onBlur={handleBlur}
value={values.email}
onChange={handleChange}
error={touched.email && Boolean(errors.email)}
helperText={touched.email && errors.email}
/>
<label className="userlabels">Password</label>
<TextField
sx={{
width: "55%",
marginBottom: "4vh",
}}
name="password"
placeholder="password"
type={passwordShown ? "text" : "password"}
id="password"
autoComplete="off"
onBlur={handleBlur}
value={values.password}
onChange={handleChange}
error={touched.password && Boolean(errors.password)}
helperText={touched.password && errors.password}
/>
<FormControl>
<label className="userlabels" id="role-label">
Role
</label>
<Select
name="role"
labelId="role-label"
id="role"
onChange={handleChange}
onBlur={handleBlur}
value={values.role}
error={touched.role && Boolean(errors.role)}
>
{role.map((option, index) => (
<MenuItem key={index} value={option.role}>
{option.role}
</MenuItem>
))}
</Select>
<FormHelperText error>
{touched.role && errors.role}
</FormHelperText>
</FormControl>
<label className="userlabels">Organization</label>
<TextField
sx={{
width: "55%",
marginBottom: "4vh",
}}
name="organ"
placeholder="Organization"
type="text"
id="organ"
onBlur={handleBlur}
value={[values.organ]}
onChange={handleChange}
error={touched.organ && Boolean(errors.organ)}
helperText={touched.organ && errors.organ}
/>
<FormControl>
<label className="userlabels" id="active-label">
Active
</label>
<Select
name="active"
labelId="active-label"
id="active"
onChange={handleChange}
onBlur={handleBlur}
value={values.active}
error={touched.active && Boolean(errors.active)}
>
{active.map((option, index) => (
<MenuItem key={index} value={option.id}>
{option.active}
</MenuItem>
))}
</Select>
<FormHelperText error>
{touched.active && errors.active}
</FormHelperText>
</FormControl>
<Button
variant="contained"
type="submit">
Update
</Button>
</form>
and this is formik code
const initialValues = {
fullName: "",
email: "",
userName: "",
organ: [] ,
password: "",
role: [],
active: "",
};
const { values, errors, touched, handleBlur, handleChange, handleSubmit } =
useFormik({
initialValues,
validationSchema: UserSchema,
onSubmit: (values, action) => {
fetch(`${state.baseUrl}/users/${id}`, {
method: "PATCH",
body: JSON.stringify({
fullname: values.fullName,
email: values.email,
username: values.userName,
organization: [values.organ],
password: values.password,
role: [values.role],
is_active: values.active,
}),
headers: {
"Content-type": "application/json",
},
})
.then((response) => {
if (response.status === 200) {
setAlert(true);
setMsg("You Are Successfully Updated the User");
setType("success");
return Promise.resolve();
} else {
setAlert(true);
setMsg("Some Thing Went Wrong , Please Try Again Later.");
setType("error");
return Promise.reject("failed");
}
})
.catch((error) => {
setAlert(true);
setMsg("Some Thing Went Wrong , Please Try Again Later.");
setType("error");
console.error("There was an error!", error);
});
action.resetForm();
},
});
i want to get pre-filled form and updated on submission

How do I handle Formik (React) onSubmit correctly for asynchronousity?

How can I prevent onSubmit from being triggered if the form is already submitting?
If I add something like if (isSubmitting) return, Formik will treat that as the Promise being resolved and set isSubmitting to false immediately (even if there is a previous Promise running).
import React from 'react';
import ReactDOM from 'react-dom';
import { Formik, Field, Form } from 'formik';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const Example = () => (
<div>
<h1>Sign Up</h1>
<Formik
initialValues={{
firstName: '',
lastName: '',
email: '',
}}
onSubmit={async (values) => {
console.log("submitting!!")
await sleep(1000);
}}
>
{({ isSubmitting }) => (
<Form>
<label htmlFor="firstName">First Name</label>
<Field name="firstName" placeholder="Jane" />
<label htmlFor="lastName">Last Name</label>
<Field name="lastName" placeholder="Doe" />
<label htmlFor="email">Email</label>
<Field name="email" placeholder="jane#acme.com" type="email" />
<button type="submit">
Submit
</button>
</Form>
)}
</Formik>
</div>
);
ReactDOM.render(<Example />, document.getElementById('root'));
In this case, you could add disabled to button whenever isSubmitting is true. So that user cannot click if isSubmitting process is ongoing.
<Formik
initialValues={{
firstName: "",
lastName: "",
email: ""
}}
onSubmit={async (values) => {
console.log("submitting!!");
await sleep(1000);
}}
>
{({ isSubmitting }) => (
<Form>
<label htmlFor="firstName">First Name</label>
<Field name="firstName" placeholder="Jane" />
<label htmlFor="lastName">Last Name</label>
<Field name="lastName" placeholder="Doe" />
<label htmlFor="email">Email</label>
<Field name="email" placeholder="jane#acme.com" type="email" />
<button disabled={isSubmitting} type="submit">
Submit
</button>
</Form>
)}
</Formik>
Furthermore if you want to have more control onisSubmitting props, you could manipulate it with setSubmitting in the onSubmit process.
<Formik
initialValues={{
firstName: "",
lastName: "",
email: ""
}}
onSubmit={async (values, {setSubmitting}) => {
setSubmitting(true) // if you want more control
console.log("submitting!!");
await sleep(1000);
setSubmitting(false) // if you ant more control
}}
>
...
</Formik>

TypeError: Cannot read property 'style' of null on TextareaAutosize

I am having trouble with unit snapshot testing and cant figure why.
I am getting the following error:
enter image description here
The component is actually a Form with 3 fields: Full name, Email and Note. There is also a checkbox which controls the Email and Note field in case we dont want to put that information in the form.
For the Note I am using TextareaAutosize, so that the text area is re sizable (due to requirements).
const [missingInfo, setMissingInfo] = useState(false);
const initialValues = {
fullName: '',
email: '',
note: '',
};
return (
<Formik
initialValues={initialValues}
validationSchema={object({
fullName: string().required('Must contain a full name'),
})}
onSubmit={(values, { setSubmitting }) => {
setSubmitting(true);
...
}}
>
<Form>
<Typography>Title</Typography>
<HashLink
to={{ hash: '#xxx' }}
onClick={() => toggleHash()}
>
Changed your mind?
</HashLink>
<Typography>Full name</Typography>
<Field name="fullName">
{({ field }: FieldProps) => (
<TextField
id="fullName"
variant="outlined"
type="text"
fullWidth
{...field}
/>
)}
</Field>
<ErrorMessage name="fullName">
{msg => <div className={styles.errorMessage}>{msg}</div>}
</ErrorMessage>
<FormControlLabel
control={
<Checkbox
checked={missingInfo}
onChange={() => setMissingInfo(!missingInfo)}
name="missingInfo"
/>
}
label="No info?"
/>
{!missingInfo && (
<>
<Typography>Email</Typography>
<Field name="email">
{({ field }: FieldProps) => (
<TextField
id="email"
variant="outlined"
type="text"
fullWidth
{...field}
/>
)}
</Field>
<Typography>Note</Typography>
<Field name="note">
{({ field }: FieldProps) => (
<TextareaAutosize
rowsMin={3}
className={styles.textArea}
{...field}
/>
)}
</Field>
</>
)}
<Button
variant="contained"
type="submit"
>
Apply
</Button>
</Form>
</Formik>
)
The error is as I said in the snapshot test:
test('matches snapshot', () => {
const tree = renderer
.create(
<BrowserRouter>
<MyComponent {...props} />
</BrowserRouter>,
)
.toJSON();
expect(tree).toMatchSnapshot();
});
Can someone please help?

Formik OnSubmit Not Getting Triggered

I am working on a react project and i am using Formik for forms. For some reason, the onSubmit method doesnt get triggered. I have been trying to figure out where the error is but i have couldnt.
I have an almost similar setup on another component and that works fine. Any help is appreciated.
There are no issues with validation, because the button becomes enabled only if it satisfies the validation parameters. I click the submit button and nothing happens. I have tried debugging but its not even hitting the onSubmit method.
<Segment>
<Formik
initialValues={{
comments: "",
mk1: "",
mk2: "",
mk3: "",
mk4: "",
mk1Num: 0,
mk2Num: 0,
mk3Num: 0,
mk4Num: 0,
}}
validationSchema={Yup.object({
comments: Yup.string().required(),
mk1: Yup.string().required(),
mk2: Yup.string().required(),
mk3: Yup.string().required(),
mk4: Yup.string().required(),
mk1Num: Yup.number().positive().max(5),
mk2Num: Yup.number().positive().max(5),
mk3Num: Yup.number().positive().max(5),
mk4Num: Yup.number().positive().max(5),
})}
onSubmit={async (values, { setErrors, setSubmitting }) => {
try {
console.log(values);
const totalGrade =
values.mk1Num + values.mk2Num + values.mk3Num + values.mk4Num;
await addToSubmittedAssignment(
courseId,
assignmentId,
values,
totalGrade
);
setSubmitting(false);
} catch (error) {
setErrors({ err: error.message });
}
}}
>
{({ isSubmitting, isValid, dirty, errors, values }) => (
<Form className='ui form'>
<TextArea
name='comments'
placeholder='Enter Comments Here'
rows={10}
/>
<Grid>
<Grid.Column width={14}>
<TextInput name='mk1' placeholder='Enter Marking Key 1' />
<TextInput name='mk2' placeholder='Enter Marking Key 2' />
<TextInput name='mk3' placeholder='Enter Marking Key 3' />
<TextInput name='mk4' placeholder='Enter Marking Key 4' />
</Grid.Column>
<Grid.Column width={2}>
<TextInput name='mk1Num' type='number' />
<TextInput name='mk2Num' type='number' />
<TextInput name='mk3Num' type='number' />
<TextInput name='mk4Num' type='number' />
</Grid.Column>
</Grid>
{errors.err && (
<Label
basic
color='red'
style={{ marginBottom: 10 }}
content={errors.err}
/>
)}
<br />
<Button
loading={isSubmitting}
disabled={!isValid || !dirty || isSubmitting}
type='submit'
fluidsize='large'
color='teal'
content='Submit'
/>
</Form>
)}
</Formik>
</Segment>
Also, i am using Semantic UI react for styling.
Try adding the submit method to Form render params:
{({ isSubmitting, submitForm, isValid, dirty, errors, values }) => (...
And then call it on submit button:
<Button
loading={isSubmitting}
disabled={!isValid || !dirty || isSubmitting}
type='submit'
fluidsize='large'
color='teal'
content='Submit'
onClick={submitForm}
/>
I had this issue and it turned out to be Yup silent failing a validation check. I simply removed the .required() from some of the object's properties to get it to work.

How to append inputs with formik in react

So this may be something simple but I'm hitting a roadblock. I want to take in a form input but can't seem to figure out how to append the value correctly so its appending string is captured.
I'm using Formik with yup in react.
<InputGroup>
<Field
id="appended-input"
name="domain_url"
type="text"
value={values.domain_url}
className={
"form-control" +
(errors.domain_url && touched.domain_url
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="domain_url"
component="div"
className="invalid-feedback"
/>
<InputGroupAddon addonType="append">
.localhost
</InputGroupAddon>
</InputGroup>
Any help would be appreciated. I just want to get the .localhost to be automatically added to the input items. for this field. I thought I could do something like value=({values.domain_url} + ".localhost") but that didn't seem to work and as you may already tell I am very new to javascript.
Thank you!
Full code below, I'm also having issues with datepicker displaying within the formik state, and then there's how to even get the values to push to my getTenant(function) to be passed to my api.
static propTypes = {
addTenant: PropTypes.func.isRequired,
};
onSubmit = (values) => {
values.preventDefault();
this.props.addTenant(values);
};
render() {
const {
domain_url,
schema_name,
name,
config,
} = this.state;
const TenantSchema = Yup.object().shape({
domain_url: Yup.string()
.max(255, "Must be shorter than 255 characters")
.required("Client URL header is required"),
schema_name: Yup.string()
.max(255, "Must be shorter than 255 characters")
.required("Client db name is required"),
name: Yup.string()
.max(255, "Must be shorter than 255 characters")
.required("Client name is required"),
});
return (
<div className={s.root}>
<Formik
initialValues={{
domain_url: "",
schema_name: "",
client_name: "",
config: [
{
date: "",
Tenant_description: "",
},
],
}}
// validationSchema={TenantSchema} this is commented off because it breaks
submittions
onSubmit={(values, { setSubmitting, resetForm }) => {
setSubmitting(true);
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
resetForm();
setSubmitting(false);
}, 100);
}}
//onSubmit={onSubmit}
>
{({
values,
errors,
status,
touched,
handleBlur,
handleChange,
isSubmitting,
setFieldValue,
handleSubmit,
props,
}) => (
<FormGroup>
<Form onSubmit={handleSubmit}>
<legend>
<strong>Create</strong> Tenant
</legend>
<FormGroup row>
<Label for="normal-field" md={4} className="text-md-right">
Show URL
</Label>
<Col md={7}>
<InputGroup>
<Field
id="appended-input"
name="domain_url"
type="text"
value={values.domain_url}
onSubmit={(values) => {
values.domain_url = values.domain_url + ".localhost";
}} //this isn't working
className={
"form-control" +
(errors.domain_url && touched.domain_url
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="domain_url"
component="div"
className="invalid-feedback"
/>
<InputGroupAddon addonType="append">
.localhost
</InputGroupAddon>
</InputGroup>
</Col>
</FormGroup>
<FormGroup row>
<Label for="normal-field" md={4} className="text-md-right">
Database Name
</Label>
<Col md={7}>
<Field
name="schema_name"
type="text"
className={
"form-control" +
(errors.schema_name && touched.schema_name
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="schema_name"
component="div"
className="invalid-feedback"
/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="normal-field" md={4} className="text-md-right">
Name
</Label>
<Col md={7}>
<Field
name="name"
type="text"
className={
"form-control" +
(errors.name && touched.name
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="name"
component="div"
className="invalid-feedback"
/>
</Col>
</FormGroup>
<FieldArray
name="config"
render={(arrayHelpers) => (
<div>
{values.config.map((config, index) => (
<div key={index}>
<FormGroup row>
<Label
md={4}
className="text-md-right"
for="mask-date"
>
Tenant Description
</Label>
<Col md={7}>
<TextareaAutosize
rows={3}
name={`config.${index}.tenant_description`}
id="elastic-textarea"
type="text"
onReset={values.event_description}
placeholder="Quick description of tenant"
onChange={handleChange}
value={values.tenant_description}
onBlur={handleBlur}
className={
`form-control ${s.autogrow} transition-height` +
(errors.tenant_description &&
touched.tenant_description
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="tenant_description"
component="div"
className="invalid-feedback"
/>
</Col>
</FormGroup>
<FormGroup row>
<Label
for="normal-field"
md={4}
className="text-md-right"
>
Date
</Label>
<Col md={7}>
<DatePicker
tag={Field}
name={`config.${index}.date`}
type="date"
selected={values.date}
value={values.date}
className={
"form-control" +
(errors.date&& touched.date
? " is-invalid"
: "")
}
onChange={(e) =>
setFieldValue("date", e)
}
/>
<ErrorMessage
name="date"
component="div"
className="invalid-feedback"
/>
</Col>
</FormGroup>
</div>
))}
</div>
)}
/>
<div className="form-group">
<button
type="submit"
disabled={isSubmitting}
className="btn btn-primary mr-2"
>
Save Tenant
</button>
<button type="reset" className="btn btn-secondary">
Reset
</button>
</div>
</Form>
<Col md={7}>{JSON.stringify(values)}</Col>
</FormGroup>
)}
</Formik>
</div>
);
}
}
export default connect(null, { addTenant })(TenantForm);
You could use setFieldValue if you want to customize the value
https://jaredpalmer.com/formik/docs/api/formik#setfieldvalue-field-string-value-any-shouldvalidate-boolean--void
You can add onChange
<Field
id="appended-input"
name="domain_url"
type="text"
value={values.domain_url}
onChange={st => {
let value = st.target.value;
let suffix = ".localhost";
let index = value.indexOf(".localhost");
if (index > 0) {
suffix = "";
}
//add suffix 'localhost' if it is not already added
props.setFieldValue("domain_url", value + suffix);
}}
className={
"form-control" +
(errors.domain_url && touched.domain_url ? " is-invalid" : "")
}
/>;
But adding suffix is more preferable on onSubmit:
onSubmit = {(values, actions) => {
console.log('valuesbefore',values)
values.domain_url= values.domain_url+ ".localhost"
console.log('valuesafter',values)
this.props.addTenant(values);
};

Categories