Using lodash debounce to return a promise
Asked Answered
P

1

14

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;
Pallet answered 15/8, 2019 at 10:41 Comment(1)
A solution without lodash - 30secondsofcode.org/js/s/debounce-promiseSerranid
G
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.

Gratian answered 15/8, 2019 at 10:56 Comment(6)
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)Marginal
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 #29132061 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: codesandbox.io/s/loving-hamilton-nip85?file=/src/Form.jsMarginal
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: codesandbox.io/s/young-fast-12182?file=/src/Form.jsMarginal
@TylerRickYes you are right. I mainly wanted to show the code itself but did not test it. I updated my link. TyGratian
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 functionUngodly
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. codesandbox.io/s/condescending-chatelet-8lpkiy?file=/src/…Halogenate

© 2022 - 2024 — McMap. All rights reserved.