Skip to content Skip to sidebar Skip to footer

Why My Debounce Functionality Is Not Working In A React App With Hooks?

My goal is to fire a fetch request to get data from an API after some delay. In my particular case, I have an input field where a user can input a post ID. I don't want fire a fetc

Solution 1:

The main issue here is that you are calling the function only when input != "". Till then input is not set to the desired value and thus the function becomes a no-op. This change will however help

import { useCallback, useEffect, useState } from 'react'
import './App.css'

function debounce(fn, ms) {
  let timeoutId
  return function (...args) {
    if (timeoutId) clearTimeout(timeoutId)
    timeoutId = setTimeout(() => {
      fn(...args)
    }, ms)
  }
}

export default function App() {
  const [input, setInput] = useState('')

  const handleInputChange = (e) => {
    setInput(e.target.value);
    debouncedFireRequest(e.target.value);
  };

  const debouncedFireRequest = useCallback(
    debounce(async (value) => {
      if (value !== '') {
        let res = await fetch(`https://jsonplaceholder.typicode.com/posts/${value}`)
        let resData = await res.json()
        console.log(resData)
      }
    }, 1000),
    []
  );

  return (
    <div className='App'>
      <input type='text' onChange={handleInputChange} value={input}></input>
    </div>
  );
}

Solution 2:

Try this:

useEffect(() => {
    const apiCallTimeoutId = setTimeout(() => {
      fireRequest()
    }, 1000);
    return () => clearTimeout(apiCallTimeoutId);
  }, [input]);

1 sec after the user stops typing the request will be fired.


Solution 3:

This answer is on the right track but there's no need to keep const fireRequest = useCallback(debounce(fetchData, 1000), []) as it seems to imply.

Just fire the timeout, then clear it in the useEffect cleanup callback if the component rerenders:

<script type="text/babel" defer>
const {useState, useEffect} = React;

const Example = () => {
  const [input, setInput] = useState("");
  const [result, setResult] = useState();

  const fetchData = async () => {
    if (input) {
      try {
        const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${input}`);
        
        if (res.ok) {
          setResult(await res.json());
        }
      }
      catch (err) {
        console.error(err);
      }
    }
  }

  useEffect(() => {
    const timeout = setTimeout(fetchData, 1000);
    return () => clearTimeout(timeout);
  }, [input]);

  return (
    <div>
      <input 
        placeholder="type a small number, e.g. 12"
        onChange={e => setInput(e.target.value)} 
        value={input}
      />
      {result && <div>{result.title}</div>}
    </div>
  );
};

ReactDOM.render(<Example />, document.body);

</script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

Note the try-catch on the fetch call and checking res.ok rather than throwing when res.json() fails.


Post a Comment for "Why My Debounce Functionality Is Not Working In A React App With Hooks?"