DEV Community

How to debounce and throttle in React without losing your mind

Nadia Makarevich on January 12, 2023

Originally published at https://www.developerway.com. The website has more articles like this 😉 When talking about performance in general, an...
Collapse
 
brense profile image
Rense Bakker

Nice journey! I like your thought process, it's a sign of a good developer 👍 btw, since React 18, you can also make use of the new built-in useDeferredValue hook:

function Input() => {
  const [value, setValue] = useState()
  const deferredValue = useDeferredValue(value)

  useEffect(() => {
    console.log(deferredValue)
  }, [deferredValue])

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

  return <input onChange={onChange} value={value} />
}
Enter fullscreen mode Exit fullscreen mode

Changes to deferredValue are delayed while the component is rendering (like during typing, because value keeps changing).

Collapse
 
adevnadia profile image
Nadia Makarevich

Oh, interesting! Haven't used it before, will definitely try, thank you!

Collapse
 
kushalmahajan profile image
Kushal V. Mahajan • Edited

I also notice that @brense has applied useCallback to onChange handler. Carrying forward @adevnadia 's logic of taking the sendRequest and debounced callback assignment outside the component due to state re-renders. For the same reason, would it also make sense to always apply useCallback to the onChange handler whenever an input field is in play with useState?

Collapse
 
brense profile image
Rense Bakker

You don't strictly have to use useCallback, but its good practice to memoize your function assignments. If you use the function as parameter for another hook, like a useEffect, you need to memoize, otherwise your side effect will always trigger on every render.

Thread Thread
 
kushalmahajan profile image
Kushal V. Mahajan • Edited

Since any state change will recreate all handlers. onChange, onSubmit etc then my first question is, why not strictly use it for all such handlers?
Second, in this case, onChange is not passed as function parameter for another hook. So why useCallback?

I do have an understanding of useCallback in general and you have very rightly said the same. However, this state re-render has just popped up a question that "shouldn't we memoize each function defined inside component if it has state changes happening".

Hope, I make sense!

Thread Thread
 
brense profile image
Rense Bakker

Yes I agree we should always memoize functions, but there is a large group of React developers who disagree. Their main argument is that memoization causes overhead, although I have not seen any benchmarks that proof this claim.

Collapse
 
nssimeonov profile image
Templar++

Sadly we can't upgrade to React18... yet...

Collapse
 
nssimeonov profile image
Templar++ • Edited

Awesome explanation! We hit our heads in nearly the same wall a while ago, but I was a beginner in react back then and it took me a couple of days to figure out what the hell was happening with the state. Then we invented almost the same useDebounce... and discovered this awesome lib, which we are using it for over a year and it works great:

github.com/xnimorz/use-debounce

I mean - there isn't a need to reinvent the wheel, when others did it already, right?

Collapse
 
adevnadia profile image
Nadia Makarevich

Technically there isn't, and the library is good, I agree :) But it's good to understand what exactly is happening and why, so that it's possible to make an informed decision, whether an additional library is needed or not.

Adding an external dependency to a project is never "free", especially in the modern frontend. So if a project already uses lodash or lodash-like library, then I personally wouldn't add a dependency on something that can be replaced with just one simple hook.

Collapse
 
nssimeonov profile image
Templar++

Agreed. Understanding why you get stale state is very important. You can run into the same issue in many different ways, regardless if you use lodash or not, but with timers one has to be really careful.

Adding precisely this external dependency instead of using lodash has more advantages, but I agree, that everyone is free to decide what libs to use. I personally would avoid adding lodash or momentum to any customer-facing frontend.

Thread Thread
 
adevnadia profile image
Nadia Makarevich

Curious, why you wouldn't use lodash?

Thread Thread
Collapse
 
stefandjokic profile image
Stefan Đokić

Amazingly written, Nadia! I enjoyed reading it, and I will definitely save this for future uses. This thought process, and especially the interactive examples really nailed it.

Just in the past year, I had multiple cases on two projects where I dealt with this exact "issue", and I spent a lot of time figuring it out, eventually ending up at a similar place as your final code.

Even though React 18's useDeferredValue hook is an alternative, there are also going to be use cases in which a throttle will be required.

Collapse
 
synthetic_rain profile image
Joshua Newell Diehl

Wow. A thorough, thoughtful, well-written analysis of an actionable technique ... Thanks so much, Nadia!

Collapse
 
gilfewster profile image
Gil Fewster

This post is an excellent journey. A great read and very thorough.

Collapse
 
andreyen profile image
Andrey Smirnov

Easy throttle and debounce explanation: dev.to/andreyen/how-to-use-throttl...