Focus On Input With Error on Form Submission - reactjs

It would be nice to have functionality when the user submits a form that fails the validation, it will set focus on the first field with an error.
I have shouldFocusError : true set in the useForm configuration. When I click submit, the validation triggers, but the input is not focused.
Any suggestions on how I can achieve this?
For reference, I am using MUI and react-hook-form to create custom components in the form. For the components themselves, they are using the useController hook like so:
const { field, fieldState } = useController(props);
<TextField
{...field}
error={!!fieldState.error}
helperText={fieldState.error?.message || ''}
/>

Solution:
Spreading the field object returned from the useController hook contains the following:
{
value: ...
name: ...
onChange: ...
onBlur: ...
ref: ...
}
The problem I described above was solved when I removed ref property from the object and passed it separately to inputRef. Like so:
const { field, fieldState } = useController(props);
const { ref, ...fieldProps } = field;
<TextField
{...fieldProps}
inputRef={ref} //or you can use field.ref
id={field.name} //this solved other issues I was having
error={!!fieldState.error}
helperText={fieldState.error?.message || ''}
/>
After doing this, React Hook Form set focus on the first field with an error when the user submits a form that fails the validation.

Related

react MUI TextField inside react-hook-form Controller inside MUI Stepper-Dialog setValue not working

I have a Button that opens a MUI Dialog.
Inside the Dialog I have a MUI Stepper. My Form is split up into different parts. Some Inputs are required others are not.
//Example Input
<Controller
name="stateName"
control={control}
rules={{ required: true }}
render={({ field: { onChange, value } }) => (
<TextField
required
label="stateName"
variant="standard"
onChange={onChange}
value={value}
fullWidth
error={errors.stateName ? true : false}
helperText={errors.stateName ? "Pflichtfeld" : null}
/>
)}
/>
Full Example: https://codesandbox.io/s/gracious-tdd-dkzoqy
When I submit my form I add an entry to an existing list and display it alongside with an edit-Button.
If the edit-Button gets pressed I want to open the Dialog and have the Inputs filled with the values of the edited data.
I tried using react-hook-form setValue("field", value) but it is not working.
I also tried to pass the edit-object via Props to the nested form-steps and use setValue inside these components useEffect utilizing useFormContext() but it didn't work either.
How can I pass the values to the Inputs so they get correctly displayed in the Multi-Step-Form-Dialog?
Working CSB -> https://codesandbox.io/s/eloquent-chaum-znt71c?file=/src/App.tsx
In editHandler, state is a json string, so the simplest fix is to parse it into the object
const editHandler = (stateJSON: any) => {
const state = JSON.parse(stateJSON)
methods.reset(state);
But in submitHandler data is stringified, the submitHanlder should look smth like this:
const submitHandler = (data: any) => {
setContent(prevContent => [...prevContent,data] );
methods.reset();
setEditState(undefined);
setOpen(false);
};
Also checkout this out https://beta.reactjs.org/learn/updating-objects-in-state
and
how to avoid mutations https://www.educative.io/courses/simplifying-javascript-handy-guide/B6yY3r7vEDJ

React Hook Form: Display helper text on "onBlur" while in "onChange" validation mode? [duplicate]

I use react-hook-form in onChange mode. I enable/disable the submit button depending on the validation state. It works just fine.
Now I have to switch to onBlur mode. And the current solution is not working as expected anymore. When all the field became valid I expect the submit button became enabled immediately. Now it only became enabled when I click out of the last field. What should I change in my code to work as expected?
const { register, errors, handleSubmit, formState } = useForm({
mode: "onChange" // I want to change it to onBlur
});
const { isValid } = formState;
...
<input disabled={!isValid} type="submit" />
Here is an example: https://codesandbox.io/s/react-hook-form-disable-submit-button-onblur-0293x?file=/src/index.js
UPDATE WITH SOLUTION:
NearHuscarl's answer helped to find the solution.
I have to stay at the onChange mode and handle the display of the error message myself. I only displaying the error if the field is in the touchedFields object of the formState.
const form = useForm({
mode: "onChange" // I want to change it to onBlur
});
const { register, handleSubmit, formState } = form;
const { isValid, touchedFields, errors } = formState;
...
<div>
<label htmlFor="firstName">First Name</label>
<input
name="firstName"
placeholder="bill"
{...register("firstName", { minLength: 3 })}
/>
{errors.firstName && touchedFields.firstName && (
<p>"minimum length is 3"</p>
)}
</div>
Working example: https://codesandbox.io/s/react-hook-form-disable-submit-button-onblur-forked-v8m05?file=/src/index.js
That is exactly how onBlur mode works. From the docs:
Validation will trigger on the blur event.
So when all of your fields become valid, it does not trigger validation. But once you click outside of the currently focused field, the blur event of that field fires, which runs and passes the validation check, only then the submit button is enabled again.
The solution is to change it back to onChange mode to trigger validation every time the input value changes.

Changing Autocomplete value using react-hook-form (Material-UI)

I have two Autocomplete fields, my goal is to change the value of the second field by the value of the first field.
The problem I face is when my trying to send the new value to the "setValue" function nothing happens the state form changing, but the autocomplete field shows the old value.
In this sand box:
https://codesandbox.io/s/dazzling-carson-84dxp?file=/src/Form/Components/UserCountry.js
you can see my implementation.
If you look at console when you change user_name field, you can see materialUI warning in the console which says:
Material-UI: A component is changing the uncontrolled value state of Autocomplete to be controlled.
it's reason is that your user_country's value by default is undefined and material consider this field as uncontrolled field, it means material will take the control of user_country value. To solve the problem you have two solutions:
1- Defining your form by defaultValues options like this:
const methods = useForm({
defaultValues: {
user_name: null,
user_country: null
}
});
2- Sending null as value to AutoComplete when it doesn't have any value, like this:
<Controller
control={control}
name={`user_country`}
render={({ field: { onChange, value } }) => (
<Autocomplete
id="country-select-demo"
options={countries}
onChange={(event, item) => {
onChange(item);
}}
value={value || null}
...
/>
)}
/>
Here You can see your form which updates user_country whenever you change user_name field.

react-hook-form custom validation message not showing

I'm using react-hook-form 6.8.2 with React 16.13.1 and normal validation works as expected, but not when inside of the "validate" key.
const { trigger, formState, watch, reset } = useFormContext({
mode: 'all',
});
--
ref={register({
required: 'This is required',
minLength: {
value: 3,
message: 'Length must be 3 or more',
},
validate: {
always: (value) => value === 1 || 'This should almost always trigger',
},
maxLength: {
value: 5,
message: 'max 5',
},
})}
required, minLength and maxLength all work, but always doesn't.
I have tried this:
always: (value) => value === 1 || console.log('This should almost always trigger') which logs the error in the console
and I have tried this: validate: (value) => value === 1 || 'This should almost always trigger' which does nothing either
Why isn't the validation message shown in the UI?
I made a CodeSandbox with your requirements using react-hook-form#6.8.2 and react#16.13.1 also using RHF's useFormContext hook. It's working fine, have a look:
One thing that isn't correct in your example code: you're passing the useForm config options to the useFormContext hook. That isn't working, you have to pass them instead to your useForm call. Check the CodeSandbox to see how to set the mode to 'all'.
Turns out that there was a custom error rendering class, hidden deep within our own library for input fields, that didn't account for custom error types.
:(
In: V7
Instead of passing the register function's result to a ref, just call the register function with a spread operator on the input field. After this, you need a unique name for this input, just like for every input. In my example, your config works fine. https://codesandbox.io/s/react-hook-form-js-forked-js0il?file=/src/App.js
Leave this answer for someone who looking for a similar solution.
The validate function checks for your condition. If it returns false then the message can be displayed using the error mechanism like this:
import React from "react";
import { useForm } from "react-hook-form";
const Example = () => {
const { handleSubmit, register, errors } = useForm();
const onSubmit = values => console.log(values);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input
{...register("username", {
validate: value => value !== "admin" || "Nice try!"
})}
/>
{errors.username && errors.username.message} //Here we display the error
<button type="submit">Submit</button>
</form>
);
};

React-Datepicker with Formik and Yup: Date value not validated on first blur, other than .required()

I'm using React-Datepicker inside a Formik form validated with Yup. To integrate React-Datepicker inside Formik I used the wrapper solution in this thread.
When a value is initially entered, .required() gets checked, but any other validation does not get checked. So I'm not getting my StartDate later than today validation right away. These other validations do get triggered if I re-enter the control a second time and exit it. So the only initial validation error that happens immediately is .required(), but for any other errors I need to re-touch the control. Are there any solutions to this?
EDIT: I noticed that Formik's .touched does not get set on the 1st DatePicker input. It only gets set on subsequent touches/inputs.
Yup
startDate: yup.date().nullable().required('Start Date is required')
.min(new Date(), 'Start Date must be later than today'),
DatePicker Wrapper -see this thread - credit to ToolmakerStever
I tried adding onCalendarClose to force re-validation per this DatePicker issue report, and it indeed may be happening, but this could also be a Yup Schema issue -- I'm not sure.
const { setFieldValue, validateField, values, handleBlur } = useFormikContext();
return (
<DatePicker
{...field}
{...props}
showMonthDropdown
showYearDropdown
selected={(field.value && new Date(field.value)) || null}
onChange={val => {
setFieldValue(field.name, val);
}}
onCalendarClose={val => {
// Force-validate onCalendarClose to avoid any potential DatePicker Blur issues
// Didn't help
validateField(props.name);
}}
onBlur={e => {
// Call Formik's handleBlur
// Didn't help
handleBlur(e);
}}
/>
)
I found a fix. Something wasn't marking the DatePicker as touched the first time. DatePicker exposes an event called onChangeRaw where I force-touched the control. Now all errors are triggered right away without re-touching.
onChangeRaw={e => {
setFieldTouched(field.name, true, true);
}}
This goes inside the DatePicker Wrapper for Formik, as shown in this post.
React-Datepicker has a bug with Blur for some reason, documented here. Their Blur recommendations include calling onCalendarClose or onChangeRaw. But only onChangeRaw worked reliably for me.
Creating a wrapper with Field works fine . The validations are getting triggered without any issues. Here is the sandbox.
Formik DatePicker Example
For those using react-hook-form with yup, trigger onBlur inside onChange
<Controller
name='dateOfBirth'
control={control}
render={({ field: { name, value, onChange, onBlur } }) => (
<DatePicker
selected={value}
preventOpenOnFocus={true}
maxDate={new Date()}
dateFormat="yyyy-MM-dd"
placeholderText='YYYY-MM-DD'
onBlur={onBlur}
onChange={(date: Date) => {
onChange(date);
onBlur();
}}
customInput={<DatePickerInput/>}
/>

Resources