13

Using React, react-final-form, and the lodash debounce function I would like to validate that a username has not already been used (the field is using react-final-form).

I'm having problems getting the debounce function to return a resolved promise from a fetch request.

I have provide the following codesandbox link to demonstrate my problem:

Please could anyone show me why my code is not working.

The entry point to the validation is from this.isNameUnique call referenced in the validate attribute in

import React from "react";
import { Field, Form } from "react-final-form";
import { debounce } from "lodash";

class DemoForm extends React.Component {
  getIsNameUnique = name => {
    console.log("getIsNameUnique", name);

    // fake fetch request
    return async name => {
      const asyncResponse = await new Promise(resolve =>
        setTimeout(resolve({ name: true }), 1000)
      );
      console.log("async api response", asyncResponse);
      return asyncResponse;
    };
  };

  debounceCreativeName = name => {
    console.log("debounceCreativeName", name);
    return new Promise(resolve => {
      debounce(() => resolve(this.getIsNameUnique(name)), 2000);
    });
  };

  isNameUnique = async name => {
    const isNameAvailable = await this.debounceCreativeName(name);
    console.log("isNameAvailable", isNameAvailable);
    return isNameAvailable;
  };

  render() {
    return (
      <Form
        onSubmit={() => null}
        render={({ handleSubmit, reset, submitting, pristine, values }) => {
          return (
            <form onSubmit={handleSubmit}>
              <Field name="name" validate={this.isNameUnique}>
                {({ input, meta }) => {
                  return (
                    <input
                      style={{
                        display: "flex",
                        height: "40px",
                        fontSize: "24px"
                      }}
                      autoComplete="off"
                      {...input}
                      type="text"
                      placeholder="Enter the name"
                    />
                  );
                }}
              </Field>
            </form>
          );
        }}
      />
    );
  }
}

export default DemoForm;
user2190690
  • 1,744
  • 4
  • 24
  • 31

1 Answers1

9

This sandbox fixes your problem.

You should not create a new debounce function on every render with:

return new Promise(resolve => {
  debounce(() => resolve(this.getIsNameUnique(name)), 2000);
});

Instead you should just wrap your whole function isNameUnique with the debounce (see my sandbox). By creating a new debounce function on every hit, it cannot 'remember' that is was called or that is will be called again. This will prevent the debouncing.

Additionally, by making async getIsNameUnique you can reduce the complexity of it be just using await.

Domino987
  • 8,475
  • 2
  • 15
  • 38
  • It looks like you accidentally passed `resolve()` (immediately invoking the function) directly to `setTimeout` rather than passing a function to invoke it. So it was being resolved immediately instead of after a 1000 ms delay as intended. Should be: `setTimeout(() => resolve({ name: true }), 1000)` – Tyler Rick Oct 16 '20 at 06:23
  • 1
    Also, it looks like the sandbox, as posted, is not actually doing any debouncing. Even if you type characters very quickly, the console prints out "getIsNameUnique" and "async api response" once for _every_ key press -- indicating that it hasn't been debounced at all (same symptom as https://stackoverflow.com/questions/29131561/lodash-debounce-not-debouncing but a completely different cause). Looks like the problem is that `debounce` defaults to waiting for 0 ms ... which is completely useless! Should probably be > 100. Fixed: https://codesandbox.io/s/loving-hamilton-nip85?file=/src/Form.js – Tyler Rick Oct 16 '20 at 06:41
  • Oops, I accidentally linked to your sandbox rather than my fork containing the fix. And now it won't let me edit my comment. (I always find that time limit for editing annoying.) Here's my fork: https://codesandbox.io/s/young-fast-12182?file=/src/Form.js – Tyler Rick Oct 16 '20 at 06:50
  • @TylerRickYes you are right. I mainly wanted to show the code itself but did not test it. I updated my link. Ty – Domino987 Oct 16 '20 at 13:08
  • 4
    Showing what you shouldn't do as the only code in the answer is not much of an answer, the sandbox also doesn't address how is it possible to await the result of the debounced function – Tofandel May 21 '21 at 14:45
  • This code is not right either! Even though logs are being written correctly validation does not take place! Try to make condition for specific case and return {name: false} it does not do anything. Why? Because debounce does not return anything. Solution using lodash's debounce needs a little different approach. https://codesandbox.io/s/condescending-chatelet-8lpkiy?file=/src/Form.js – vbargl Aug 12 '22 at 07:23