-
-
Notifications
You must be signed in to change notification settings - Fork 489
Debouncing Field Validation #369
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
You can use
|
@tkvw A variation of that was among the very first things I tried. Passing a debounced function as the validation function causes validation to fail completely. (That is, it will never be correctly counted as invalid or valid) |
@pmoeller91 : I see you're right: you can try this:
@see: https://codesandbox.io/s/mmywp9jl1y and if you only want to debounce the active field you can use:
|
@tkvw Looking at the codesandbox, at first I thought it wasn't quite working correctly...And it wasn't, but it was because of the timeout in the pseudo-async username validation code causing some slight strangeness. Removing the timeout in their username validation code made it work exactly as intended. I would say this is absolutely a valid solution to the problem, and definitely the first solution I have seen that actually works correctly. Very well done! I am impressed. I still feel like this is a feature worth incorporating directly into react final form. If not, at the very least, what you have created there may be worth releasing as a lightweight add-on to react-final-form. I could absolutely see myself pulling it in on projects; it absolutely can improve UX, and makes truly async field-level validation viable. I will leave this open in the time being in the hopes of at least one of those two things happening. Thank you for your responses! |
Thank you @tkvw, that is good solution! But be aware from using Form "validating" flag on your debounced input, if you want to set eg. input disabled={true} while is's validating. You must provide own "validating" prop from DebouncingValidatingField. Don't use "validating" prop that Form provide, because it will trigger to true when your component "debouncing", so you will lose input focus when type one letter. 💪 |
@l3v1k Please can you explain a bit more what you're referring to? I'm have that problem but not sure how to resolve it. Thanks! |
If you don't want to implement the promise debounce logic yourself, you might want to checkout some utility library like this one: https://github.com/sindresorhus/p-debounce |
I am still struggling to achieve this debounce behavior on gloabl form level validation. As my form is huge and i don't want to implement per field validations( I am using yup to define schema and validate against it in |
We also have the case as @ziaulrehman40. Async validation examples seem to overlook this common case: when you have a form and need to validate async only one field. Not only in Final-forms, but also in Formik, React-hook-form etc. I think, Yup is a good declarative way to validate a form. So we prefer to use it, instead of duplicating validation rules for single fields. But when there's an async validation it breaks, because Yup can't pass loading state to form's library. On the other hand, separating one field for async validation from Yup, seems pushing you to not use Yup at all. So for now, fixing this common case on my own, the best (but not great) way I see, is to have validation function which tracks async field changes, validates it, then runs common validation with Yup, and then needs to merge these results and return in one object. But this still requires a bunch of state tracking happening around the React components. I'd rather prefer to finally have a common solution to this use case. |
Thanks @artegen for your thoughts, I found the same issues and I'm now working on something related to your solution. Did you found any problem or can you share a snippet of your solution? |
Same scenario here as @ziaulrehman40 @artegen and @andtos90. Any suggestions? |
@bostrom this is my solution (you will need memoizee):
Create your validation:
Usage:
|
Interesting. |
Here's a memoized debounced version: export default function DebouncedMemoizedField({
milliseconds = 400,
validate,
...props
}) {
const timeout = useRef(null);
const lastValue = useRef(null);
const lastResult = useRef(null);
const validateField = (value, values, meta) => new Promise((resolve) => {
if (timeout.current) {
timeout.current();
}
if (value !== lastValue.current) {
const timerId = setTimeout(() => {
lastValue.current = value;
lastResult.current = validate(value, values, meta);
resolve(lastResult.current);
}, milliseconds);
timeout.current = () => {
clearTimeout(timerId);
resolve(true);
};
} else {
resolve(lastResult.current);
}
});
return <Field validate={validateField} {...props} />;
} Usage: <MemoizedDebouncedValidationField
name="username"
validate={(value) => (value === 'jim' ? 'Username exists' : undefined)}
render={({ input, meta }) => (
<>
<input {...input} />
{(meta.touched && meta.error) && <p>Error</p>}
</>
)}
/> |
It is 4 in the morning. I don't know why it works and don't care at this point. Here is a debounced validation function for the whole form:
Later just pass it to form like
Thanks to @onosendi |
Are you submitting a bug report or a feature request?
Feature Request
What is the current behavior?
Currently, there is no way I can find that would allow for debouncing field-level validation. Field-level validation is very nice, but rerunning the validation on every keystroke makes for a poor user experience and lower performance.
Async field-level validation also results in firing off numerous async requests with no trivial way to debounce that, either. Even the linked example for asynchronous validation demonstrates this poor rapid-fire async behavior. (Select username field, select a different field, select username field again. Try typing: "Georgeee" at a moderate pace, and watch as the "username taken" message appears and rapidly disappears as the previous async validation returns and then is replaced by the next async validation)
Right now, even pausing the validation via the form API will result in react-final-form completely stopping all form changes until validation is resumed.
What is the expected behavior?
The ability to debounce validation. All validation should optionally be delayed until a set interval has passed without the form changing. This will reduce the pressure caused by firing numerous async validations, reduce overhead associated with validating even one field on every render, and improve the user experience by delaying error display until typing is completed. Even at 60wpm validation will be triggered on a field as frequently as 5 times every single second.
Other information
There was another issue on this same topic, but the author closed the issue and received no responses.
I have searched pretty far and wide on this issue and have not found any satisfactory answer. Some people have attempted variations of it, but as far as I can tell none have succeeded. It seems like performing field-level validation at absolutely every single render/update is deeply built into final-form itself. There is absolutely no way right now for validation to separated cleanly from the process, and no way for errors to be delivered truly asynchronously using the built-in validation functions.
I could be wrong, and maybe I am! If so, please demonstrate how you can cause a field validation to fire only once when typing stops.
Using OnBlur validation could be one potential solution to help avoid this problem, but it seems more like a bandaid than a true fix.
The text was updated successfully, but these errors were encountered: