React JS Multiple submit Buttons react-hook-form - reactjs

im using react-hook-form for form validation and submission ,everything work's fine with single submit type button, now i need to have three buttons , "Save Draft" ,"Preview Data Values in Page" ,and "Submit for approval " ,i can opt-out for a Mode Selection radio Buttons ,But wanted to have three button submit function, which needs form data . adding onchnage for input fields will work ,but form validation needs to write again .
const { register, handleSubmit } = useForm();
const onSubmit = (data) => alert(JSON.stringify(data));
function NeedTohaveFormDataHere1(Data) {
} function NeedTohaveFormDataHere2(Data) {
}
return ( <form onSubmit={handleSubmit(onSubmit)}>
<Headers />
<input name="firstName" ref={register} placeholder="First name" />
<input name="lastName" ref={register} placeholder="Last name" />
<select name="category" ref={register}>
<option value="">Select...</option>
<option value="A">Category A</option>
<option value="B">Category B</option>
</select>
<button onClick={NeedTohaveFormDataHere1}>
Save Draft
</button >
<button onClick={NeedTohaveFormDataHere2}>
Preview
</button>
<input type="submit" />
</form>
);
}
onSubmit function will get form data ,how to get form data in other two button functions ?
sloved .. with
<button onClick={handleSubmit(NeedTohaveFormDataHere1)}>
Save Draft
</button >
<button onClick={handleSubmit(NeedTohaveFormDataHere2)}>
Preview
</button>

I had the same problem and I resolved it the following way:
I have two buttons, the first bottom validates and submits the form normally. The second button only validates and calls a custom function.
I'm assuming you have the situation. One button to save and one to store a draft.
<form id="example-form" onSubmit={handleSubmit(handleOnSave)}>
<IconButtonTooltip
form="example-form"
title="Only save"
>
<SaveIcon className={classes.icon} />
</IconButtonTooltip>
<IconButtonTooltip
form="example-form"
title="Save and sign"
onClick={handleSubmit(handleOnSingAndSave)}
>
<SignatureIcon className={classes.icon} />
</IconButtonTooltip>
</form>
const handleOnSingAndSave = () => {
// Handle things...
}
It works for me!

You can use handleSubmit in multiple place.
const handleSubmitDraft=()=>{
handleSubmit(aync(data)=>{...})()
}
const handleSubmitPreview=()=>{
handleSubmit((data)=>{...})()
}
<button onClick={handleSubmitDraft}>
Save Draft
</button >
<button onClick={handleSubmitPreview}>
Preview
</button>

if you have to handle the multiple submit buttons in react-hook-form
1. remove your submit method from the form tag and add it to your button click
2. move your submit buttons outside the form tag
const { handleSubmit } = useForm();
<form>
<input />
<input />
</form>
<button onClick={handleSubmit((d) => console.log(d))} > Save </button>
<button onClick={handleSubmit((d) => console.log(d))} > Draft </button>

I used yup with resolver for validation so my answer might differ. I also used trigger to validate without submitting. I also reset the form so that it won't be marked 'dirty' after saving.
export default function MyForm(props) {
const {
control,
reset,
trigger,
handleSubmit,
getValues,
formState: { isDirty },
} = useForm({
mode: "onBlur",
reValidateMode: "onBlur",
shouldUnregister: true,
defaultValues: {
name: "",
id: "",
//rest of values
},
resolver: yupResolver(schema),
});
const handleOnSubmit = async (event) => {
if(!isDirty){
//navigate out of page
} else {
//save and exit
}
}
const handleOnSave = async () => {
trigger(); //triggers validation for whole form
const formValue = getValues();
const isValid = schema.isValidSync(formValue);
if(isValid){
//await save actions
if(savedSuccessfully){
reset(formValue);
//anything else
}
}
};
}

You can try giving name and type to a button and catch it using window.event.submitter.name.
<button type="submit" name="submit">Submit</button>
<button type="submit" name="draft">Draft</button>
<form onSubmit={handleSubmit} > ...
const handleSubmit = () => {
const buttonType=window.event.submitter.name // will return draft or submit and you can handle it using switch case.
if(buttonType==="submit"){
//HANDLE SUBMIT FUNCTION
return;
}
if(buttonType==="draft"){
//HANDLE DRAFT FUNC
return;
}
}
But this solution doesn't work in safari

I did it in the next way:
onSubmit - form submission handler
<Button type="submit">Post</Button>
<Button
appearance="stroke"
onClick={handleSubmit((data) =>
onSubmit({
...data,
status: CreateResumeRequestStatusEnum.Draft,
})
)}
>
Save for later
</Button>

Related

How to submit with React Hook Form when having mode onBlur?

This is not like what this question is asking.
Basically, I have a form that I want to auto-submit whenever fields are changed. I.e. I do not want a submit button.
So, having this:
const {
formState,
handleSubmit
} = useForm<TSaveInputSchema>({
mode: "onBlur",
reValidateMode: "onChange",
criteriaMode: "firstError",
resolver: zodResolver(saveInputSchema),
});
const handleSave = useCallback<HandleSaveCallback>(
(data) => {
console.log("DATA SUBMIT", data);
}
), []);
And HTML like this:
<form onSubmit={handleSubmit(handleSave)}>
<input {...register("name")} />
</form>
does not call the handleSave function when the input field is blurred.
How can this be done, if possible? Or do I have to manually trigger the submit through other means (e.g. using useEffect)?
Too much refactor for the day, the solution was dumb:
Replace
<form onSubmit={handleSubmit(handleSave)}>
<input {...register("name")} />
</form>
with
<input {...register("name", { onBlur:handleSubmit(handleSave) )} />

Validation for Input form component React

I have a form component that include multiple TextFields and each one have different validation and error. Assume my TextField had custom validation prop that will take some conditions and show validation either onChange or onBlur for each TextField. I also have custom error prop that display message without condition.
const Address = ({
onChange,
required,
error,
value = {},
validation = undefined,
}: AddressProps) => {
const validator = {
validationAddress: (value: string) => (value.length === 0) ? 'Address is required' : '',
validationCity: (value: string) => (value.length === 0) ? 'City is required' : '',
}
const handleChange = (event) => {
...
}
return (
<React.Fragment>
<TextField
id="address"
label="address"
type="text"
required={required}
onChange={handleChange}
value={value.address}
validationEvent="change"
validation={validator.validationAddress}
/>
<TextField
id="city"
label="city"
type="text"
required={required}
onChange={handleChange}
value={value.city}
validationEvent="change"
validation={validator.validationCity}
/>
</React.Fragment>
export default Address;
After that I will implement my Address component in an App with submit button.
const App = () => {
const handleSubmit = () => {
...
}
return (
<Address />
<button type='submit' onClick={handleSubmit}>
Submit
</button>
)
}
export default App;
I saw many examples that input and form put together with submit button but I am struggling with handle them separately this way. My task is need to handle validation in Address component but have submit button as an optional option outside the Address component. My goal is validation message should not show until Submit button is clicked.
Try this:
move validator to parent component
pass validatorMessage useState var to Address component
call the validator by the click of submit button
Now the message is managed in the parent component and you control when the submit button displays it.
I would suggest to consider a different approach: there is no need to manually implement validation, we can use the browser built-in validation feature:
https://medium.com/p/491327f985d0
Essentially I use standard HTML mark-up and properties.
Implementation example:
export default function App() {
const onSubmit = (data: FormData) => {
console.log(Object.fromEntries(data.entries()));
};
return (
<div className="App">
<Form onSubmit={onSubmit}>
<h2>A simple form</h2>
<TextInput label="Name:" id="name" name="name" required />
<TextInput
label="Email:"
id="email"
type="email"
name="email"
required
/>
<TextInput label="Address:" id="address" name="address" />
<TextInput
label="Tel:"
id="tel"
name="tel"
type="tel"
pattern="((\+44(\s\(0\)\s|\s0\s|\s)?)|0)7\d{3}(\s)?\d{6}" // don't rely on this
/>
<button>Submit</button>
</Form>
</div>
);
}
The Form component itself is very simple:
<form
action={action}
onSubmit={handleSubmit}
noValidate
className={style.form}
>
<div className={style.wrapper}>{children}</div>
</form>
It sets the noValidate attribute to prevent the default error notification to pop up.
The input boxes are wrapped in the form element.
The form element has an onSubmit event handler: it gets triggered when user clicks on the submit button OR when the user hit return using the keyboard.
At that point we use e.preventDefault(); to prevent default form submission and handle it manually.

React-hook-from form inside one. How to do indepentdend second form?

I have a main form and also have a second form block to check registered or not in one payment service.
When I click on the button Check, triggering the main form and sends data. The second form also sends data. I want to just check when I click on the button inside the second form. How to handle this, can you help?
const { register, handleSubmit, errors, control } = useForm({
mode: {
onblur,
},
});
const {
register: register2, handleSubmit: handleSubmit2, errors: errors2, control: control2,
} = useForm({
mode: {
onblur,
}
});
const onSubmit = (data) => {
console.log(data);
};
const onSubmit2 = (data) => {
console.log(data);
};
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
as={InputMask}
control={control}
mask="+\9\98 99 999 99 99"
name="phone"
id="phone"
defaultValue={phone}
required
/>
<form
onSubmit={
handleSubmit2(onSubmit2)
}
>
<Controller
as={InputMask}
control={control2}
mask="+\9\98 99 999 99 99"
name="phone_alif"
id="phone_alif"
required
/>
<button>Check</button>
</form>
<button>Send</button>
</form>
I solved. In order to not triggering the main form use onClick event listener on the Check button.
<form>
<Controller
as={InputMask}
control={control2}
mask="+\9\98 99 999 99 99"
name="phone_alif"
id="phone_alif"
required
/>
<button onClick={handleSubmit2(onSubmit2)}>
Check
</button>
</form>

Submit button doesn't work in Capacitor App

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

In React with Formik how can I build a search bar that will detect input value to render the buttons?

New to Formik and React I've built a search component that I'm having issues with the passing of the input value and rendering the buttons based on input length.
Given the component:
const SearchForm = ({ index, store }) => {
const [input, setInput] = useState('')
const [disable, setDisable] = useState(true)
const [query, setQuery] = useState(null)
const results = useLunr(query, index, store)
const renderResult = results.length > 0 || query !== null ? true : false
useEffect(() => {
if (input.length >= 3) setDisable(false)
console.log('input detected', input)
}, [input])
const onReset = e => {
setInput('')
setDisable(true)
}
return (
<>
<Formik
initialValues={{ query: '' }}
onSubmit={(values, { setSubmitting }) => {
setInput('')
setDisable(true)
setQuery(values.query)
setSubmitting(false)
}}
>
<Form className="mb-5">
<div className="form-group has-feedback has-clear">
<Field
className="form-control"
name="query"
placeholder="Search . . . . ."
onChange={e => setInput(e.currentTarget.value)}
value={input}
/>
</div>
<div className="row">
<div className="col-12">
<div className="text-right">
<button type="submit" className="btn btn-primary mr-1" disabled={disable}>
Submit
</button>
<button
type="reset"
className="btn btn-primary"
value="Reset"
disabled={disable}
onClick={onReset}
>
<IoClose />
</button>
</div>
</div>
</div>
</Form>
</Formik>
{renderResult && <SearchResults query={query} posts={results} />}
</>
)
}
I've isolated where my issue is but having difficulty trying to resolve:
<Field
className="form-control"
name="query"
placeholder="Search . . . . ."
onChange={e => setInput(e.currentTarget.value)}
value={input}
/>
From within the Field's onChange and value are my problem. If I have everything as posted on submit the passed query doesn't exist. If I remove both and hard code a true for the submit button my query works.
Research
Custom change handlers with inputs inside Formik
Issue with values Formik
Why is OnChange not working when used in Formik?
In Formik how can I build a search bar that will detect input value to render the buttons?
You need to tap into the props that are available as part of the Formik component. Their docs show a simple example that is similar to what you'll need:
<Formik
initialValues={{ query: '' }}
onSubmit={(values, { setSubmitting }) => {
setInput('')
otherStuff()
}}
>
{formikProps => (
<Form className="mb-5">
<div className="form-group has-feedback has-clear">
<Field
name="query"
onChange={formikProps.handleChange}
value={formikProps.values.query}
/>
</div>
<button
type="submit"
disabled={!formikProps.values.query}
>
Submit
</button>
<button
type="reset"
disabled={!formikProps.values.query}
onClick={formikProps.resetForm}
>
</Form>
{/* ... more stuff ... */}
)}
</Formik>
You use this render props pattern to pull formiks props out (I usually call them formikProps, but you can call them anything you want), which then has access to everything you need. Rather than having your input, setInput, disable, and setDisable variables, you can just reference what is in your formikProps. For example, if you want to disable the submit button, you can just say disable={!formikProps.values.query}, meaning if the query value in the form is an empty string, you can't submit the form.
As far as onChange, as long as you give a field the correct name as it corresponds to the property in your initialValues object, formikProps.handleChange will know how to properly update that value for you. Use formikProps.values.whatever for the value of the field, an your component will read those updates automatically. The combo of name, value, and onChange, all handled through formikProps, makes form handing easy.
Formik has tons of very useful prebuilt functionality to handle this for you. I recommend hanging out on their docs site and you'll see how little of your own code you have to write to handle these common form behaviors.

Resources