Debounce on a React functional component when typing not working

1k views Asked by At

I have a form component, and I would like to perform an API request as I am typing. I want to debounce this so I don't spam the server. Here is my code:

export default function MyItem ({ id, name }) {
  const debouncePatch = (name) => {
    debounce(patchItem.bind(this, id, name), 500)
  }
  return (
    <div>
      <form
        type='text'
        value={name}
        onChange={(evt) => debouncePatch(evt.target.value)}
      />
    </div>
)
}

The patch item does a simple patch call to the server so that the item's name updates as I am typing. It looks something like this:

export default function patchItem (id, name,) {
  return axios.patch(`${MY_BASE_URL}/${id}`, { name })
}

With the debounce it is not working at all. The patchItem function is never called. How can I fix this?

3

There are 3 answers

0
Sam R. On BEST ANSWER

You're calling debounce on every change to the input but it just creates a new debounced function every time. You need to create a single debounced function and then use it as event handler. Like this:

function MyItem({ id, name }) {
  let debouncePatch = debounce((id, name) => {
    patchItem(id, name);
  }, 500);

  // OR SIMPLER
  let debouncePatch = _.debounce(patchItem, 500);
  return (
    <div>
      <input
        type="text"
        defaultValue={name}
        onChange={(event) => debouncePatch(id, event.target.value)}
      />
    </div>
  );
}

Also make sure the input gets the defaultValue rather than value so it can be edited.

0
wentris On

Make your input controllable, and you can use simple utile use-debounce

import { useDebounce } from 'use-debounce';

export default function MyItem ({ id, name }) {
  const [value, setValue] = useState('')
  const [debouncedValue] = useDebounce(value, 1000);

  const handleChange = useCallback((e) => {setValue(e.target.value)}, [])

  useEffect(() => {
    console.log(debouncedValue);
    *// here you can place any code what you need. **console.log** will be displayed every second, or any time that you may change in hook above.* 
  }, [debouncedValue])

  return (
    <div>
      <form
        type='text'
        value={name}
        onChange={handleChange}
      />
    </div>
)
}

ps. you do not have to use bind in your example for sure. and I am not sure you can apply onChange event to form

0
Jayce444 On

There's 2 issues here. Firstly, debounce returns a function, so you need to call that one.

Secondly, if you don't memoize the debounced function then it won't work properly, as each re-render will create a new debounced function, and thus defeat the point of debouncing.

Try something like this:

const debouncePatch = useCallback(debounce(() => patchItem(this, id, name), 500), []);