Hi,
I have a form that I need to validate some fields asynchronously.
My issue is that I do not want the validation to be called every time a key press is made on the specific field, but would prefer to have it run when the user is done typing.
So what I did was wrap my async function in a debounce.
It seems to work, but my issue is after I submit and I run my submitSuccess handler, I get this error.
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application.
And it goes away if I remove the debounce.
Here is my async validation function and submitSuccess handler.
const handleAsyncValidation = debounce(async (model, error, callback) => {
const details = [];
console.log('handleAsyncValidation called')
if (!isEmpty(model.email)) {
if (await handleCheckEmailInUse(model.email) === true) {
details.push({
name: 'email',
message: i18n.t("Users.emailInUseError")
});
}
}
return callback(details.length ? { details: concat(get(error,'details',[]),details) } : error);
},800);
const handleSubmitSuccess = (currentUser) => {
console.log('handleSubmitSuccess called')
// empty client cache and redirect
props.client.resetStore().then(() => {
// set user's language
let language = get(currentUser,'language',i18n.language);
if (i18n.language !== language) i18n.changeLanguage(language);
let redirect = query.get('redirect');
if(isEmpty(redirect)){
navigateTo(CompileRoute('Route.home'));
}else{
navigateTo(decodeURIComponent(redirect));
}
});
}
Do you have any suggestions on how I could handle the async validation?
I do not want to change the validation to onSubmit because I like the fact that for any non asynchronous validation, it instantly informs the user of the errors, so I would like to keep that feature.
Hope you can guide me, thanks for your help.
Hi @simplecommerce. What uniforms version are you using? It would be great to have a minimal reproduction. I guess it shouldn't be the case in v3, but we may fix it in v2 anyway.
Hi @radekmie, thanks for the follow-up, I will try to make a working example for you so you can see what I am trying to achieve, thank you!
I am using version 2.6.7
@radekmie Just to let you know, I have done some tests and I am not able to reproduce it on a sandbox environment.
I am suspecting it is something else I am using with the library that is causing the problem.
Might be the way I am using react-apollo, I will keep testing on my end to confirm the issue.
@radekmie Hi, I have done further testing and I noticed that the issue happens when I set a cookie when the form submission calls the success handler.
It causes my form to re-render.
Is there any particular reason why the async validation would be re-called if the form itself is re-rendered?
The weird thing is I can't reproduce it on the sandbox but it happens every time in my code.
I am trying to figure out why it would trigger the async.
The only way I am able to prevent it, is by calling form.reset() in my onSubmit before it calls my onSubmitSuccess handler.
UPDATE: Ok so the issue was definitely because of the cookies being set, I am using react-cookie with universal-cookie and the withCookies HOC was wrapping my component that rendered the form, and calling the set function on the cookies, would cause it to re-render my form and for whatever reason, call the async validation.
What I did to fix it, was use React.useMemo on my form and watch for a specific prop that would require re-rendering. It seems to work now, but I stll find it odd that I was not able to reproduce this issue on the sandbox.
@simplecommerce You figured it out and that's great! The validation happens on render only when the schema/validator or model change. If you are using AutoForm, then model shouldn't change. If you are using AutoForm and manage the state (model) anyway, consider using ValidatedQuickForm or ValidatedForm instead.
@radekmie Thanks a lot for your link, I was able to really dig in and figure out the problem.
This was the cause.
if (this.props.schema !== schema || this.props.validator !== validator) {
It was going in this condition, and I noticed it was because in my custom component, I was generating the schema bridge on every render, even if in my eyes the values were the same, I suppose it differed in that instance, so I used useMemo on it and it fixed it.
Thanks again!