React Async Data fetch

A few React characteristics to keep in mind:

State Update is Asynchronous (slower than what you expect!)

  • State updates are asynchronous , so be careful with using the state immediately inside the same function. E.g. in the handleChange() below, the state input is not updated immediately after setInput is called. Therefore, if we fetch data using the input state, the value of input would actually be before the update!
  • However, the useEffect hook is run after the state updates and DOM updates, our fetchData() would get the updated input value if placed inside a useEffect hook
  • If we are calling async/ awaitfetchData() inside useEffect , this would not work inuseEffect(async ()=>{},[]) . It is because useEffect expects a clean-up function or nothing to be returned but async functions return an implicit promise! We should define the async fetchData functions separately instead of using the useEffect wrapping function
  • Since useEffect requires us to explicitly declare the dependencies in the array, it is best to define the fetchData functions and call it inside the useEffect functions so that we don’t need to worry about declaring fetchData as a dependency in useEffect !

Debounce and Re-render

Re-render = Reborn of var, func inside components

  • On each component re-render, the functions and the variables inside are created brand new and only the states are updated and preserved across the renders
  • Therefore, if we apply the debounced functions simply by itself inside a React component, debounced does not work as expected! Why?
  • Debounce works by terminating the intermediate function calls within the timeout period and therefore reduces the total number of functional invocations. e.g. if an user types ‘cat’ within a timeout of 300ms, a debounced onChange handler only invokes the function once with the input ‘cat’ because the previous invocations with input of ‘c’ and ‘ca’ are cancelled
  • In React, this presents a dilemma because… 1. on state update, the React component re-renders 2. in each re-render, functions are created brand new and executed 3. It means that, the intermediate invocations of the debounced functions still get executed as the functions are brand new in each render, as they have no memory of the time lapsed from the previous render!
  • To solve this issue, we want the same function to be preserved and called throughout the re-rendering phase; therefore, useCallback comes to rescue!
  • useCallback returns a memoized version of the function that only changes when one of the dependencies changes. If the dependency does not change, the same function would be used across re-rendering and debounce as expected!
  • This gist contains more detailed example

References

A software engineer passionate about learning and growth