Patrick Ferreira
Patrick Ferreira

Reputation: 2053

how to debounce a final-form Field?

this is a pretty common query, but I am a bit confused with the new final-form library. I used to work with redux-form but this new version is too different.

My need is simple, I want to dispatch a search as the user writes in some text, but I want to add a throttle to the Field.

Here is a first attempt with the lib react-final-form-listeners, but as you will see, when you write in the text field, the debounce does not work :/

https://codesandbox.io/embed/react-final-form-simple-example-khkof

Upvotes: 3

Views: 3325

Answers (3)

onosendi
onosendi

Reputation: 2277

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>}
    </>
  )}
/>

Upvotes: -1

Titenis
Titenis

Reputation: 805

There are multiple issues with your solution:

  • use lodash debounce instead of throttle.
  • create debounced function outside form in order to prevent its reassign on every rerender or change
  • call form submit action, instead of submit handler handleSubmit

Modified and working your example:

Edit 🏁 React Final Form - Simple Example

Upvotes: 0

Matt Carlotta
Matt Carlotta

Reputation: 19762

First, I'd encourage you to do all of this without using an obscured package layer. This will help you truly understand the flow, but nevertheless, here's how you can call a function when the input changes:

  • debounce (only executes once when a user stops typing for 500ms)
  • throttle (batches then executes every 500ms)
  • normal (executes on every input update)

In this case, I just created a debounced function outside of the render method. This varies when using classes instead of hooks:

Hooks:

const handleSearch = debounce(searchText => { ... }, 500);

Classes (or you can debounce the class field in the constructor, either work):

class Example extends Component {
  handleSearch = debounce(searchText => { ... }, 500)

  render = () => { ... }
}

Working example (type while the codesandbox console is open):

Edit 🏁 React Final Form - Simple Example


The differences between debounced, throttled, and normal execution:

enter image description here


Same as above, minus react-final-form and react-final-form-listeners (two less dependencies in your project!):

Working example (type while the codesandbox console is open):

Edit Simple Form - Simple Example


Upvotes: 0

Related Questions