Home > Blockchain >  why my code calling API for multiple time instedof just once after delaying of 500 ms using debounce
why my code calling API for multiple time instedof just once after delaying of 500 ms using debounce

Time:06-19

I'm trying to call API using debounce but in this case, API calling for every character, for example, I type hello in search then it calls for he, hel, hell, and hello but I want only for final word hello

  useEffect(() => {
    updateDebounceWord(word);
  }, [word]);

  const updateDebounceWord = debounce(() => {
    {
      word.length > 1 && dictionaryApi();
    }
  });

  function debounce(cb, delay = 500) {
    let timer = null;
    return (...args) => {
      clearTimeout(timer);
      timer = setTimeout(() => {
        cb(...args);
      }, delay);
    };
  }

const dictionaryApi = async () => {
    // inital state []
    console.log("hited")
    try {
      const data = await axios.get(
        `https://api.dictionaryapi.dev/api/v2/entries/${category}/${word}`
      );
      console.log("Fetched",word);
      setMeanings(data.data);
    } catch (e) {
      console.log("error||", e);
    }
  };

CodePudding user response:

In addition to Dilshans explanation, I wan't to suggest making a hook out of your debounce function, so you can easily reuse it:

const useDebounce = (cb, delay = 500) => {
  const timer = useRef();

  // this cleans up any remaining timeout when the hooks lifecycle ends
  useEffect(() => () => clearTimeout(timer.current), [cb, delay]);

  return useCallback(
    (...args) => {
      clearTimeout(timer.current);
      timer.current = setTimeout(() => {
        cb(...args);
      }, delay);
    },
    [cb, delay]
  );
};

use it like this in your components:

const updateDebounceWord = useDebounce((word) => {
  console.log("api call here", word);
});

useEffect(() => {
  updateDebounceWord(word);
}, [word, updateDebounceWord]);

CodePudding user response:

You are using the debounce on render phase of the component. so each time when the component rebuild a new tree due to the state update, the updateDebounceWord will redeclare. Both current and workInProgress node of the component will not share any data. If you want to share the data between current and workInProgress tree use useRef or else put in global scope

A quick fix is, put the timer variable in global scope.

// keep this on global scope
let timer = null;

function debounce(cb, delay = 500) {

  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      cb(...args);
    }, delay);
  };
}

export default function App() {
  const [word, setWord] = useState("");

  const sendReq = debounce((keyword) => {
    apiReq(keyword);
  })

  useEffect(() => {
    if (word.length > 0) {
      sendReq(word);
    }
  }, [word, sendReq])


  const apiReq = (keyword) => {
    console.log('reached', keyword);
  }

  return (
    <div className="App">
      <input value={word} onChange={(e) => setWord(e.target.value)} />
    </div>
  );
}

Also put all the dependencies in the useEffect dep array otherwise it may not work as expected.

  useEffect(() => {
    updateDebounceWord(word);
  }, [word, updateDebounceWord]);
  • Related