Common mistakes while fetching in useEffect

Fetching data in React components is a common operation, and the useEffect hook plays a crucial role in managing asynchronous tasks.

Let's see some common mistakes while fetching data in useEffect and also learn the best practices to avoid them.

Not cleaning up the effect

Not cleaning up the effect can lead to memory leaks. For example, let's say you want to subscribe to a data source

// This effect runs after every render
useEffect(() => {
  const subscription = dataSource.subscribe()
})

Make sure to unsubscribe from the data source when the component unmounts

useEffect(() => {
  const subscription = dataSource.subscribe()
  return () => {
    subscription.unsubscribe()
  }
})

Forgetting the dependency array

useEffect runs on every render by default. This can lead to performance issues. For example, let's say you want to subscribe to a data source but the data is static and won't change based on props or state.

const [data, setData] = useState(null)

useEffect(() => {
  const fetchData = async () => {
    const data = await fetch('https://api.example.com/data')
    setData(data)
  }
})

This is a easily avoidable mistake. To fix it, we need to pass an empty array as the second argument to useEffect. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run.

const [data, setData] = useState(null)

useEffect(() => {
  const fetchData = async () => {
    const data = await fetch('https://api.example.com/data')
    setData(data)
  }
}, [])

Not including all dependencies in the dependency array cause stale data

Adding unnecessary dependencies to the dependency array can also lead to bugs. For example, let's say you want to fetch data from an API based on a prop.

const [data, setData] = useState(null)

useEffect(() => {
  const fetchData = async () => {
    const data = await fetch(`https://api.example.com/data/${props.id}`)
    setData(data)
  }
}, [])

This is another common mistake. To fix it, we need to add the prop to the dependency array.

const [data, setData] = useState(null)

useEffect(() => {
  const fetchData = async () => {
    const data = await fetch(`https://api.example.com/data/${props.id}`)
    setData(data)
  }
}, [props.id])

Conditional fetching without dependencies

One other variant of same dependency array mistake is conditional fetching without dependencies. For example,

// Incorrect
let shouldFetch = true

useEffect(() => {
  if (shouldFetch) {
    fetchData()
  }
}, [])

// Correct
let shouldFetch = true

useEffect(() => {
  if (shouldFetch) {
    fetchData()
  }
}, [shouldFetch])

Not using useCallback or causing infinite loops

useCallback is a hook that returns a memoized callback. It's useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders.

For example, let's say you have a component that fetches data from an API and renders it.

const [data, setData] = useState(null)

useEffect(() => {
  const fetchData = async () => {
    const data = await fetch('https://api.example.com/data')
    setData(data)
  }
}, [])

return <div>{data}</div>

Now, let's say you want to pass the fetchData function to a child component. You can't just pass the fetchData function directly because it will be recreated on every render. This will cause the child component to re-render unnecessarily.

const [data, setData] = useState(null)

useEffect(() => {
  const fetchData = async () => {
    const data = await fetch('https://api.example.com/data')
    setData(data)
  }
}, [])

return <ChildComponent fetchData={fetchData} />

To fix this, you need to wrap the fetchData function in useCallback.

const [data, setData] = useState(null)

const fetchData = useCallback(async () => {
  const data = await fetch('https://api.example.com/data')
  setData(data)
}, [])

useEffect(() => {
  fetchData()
}, [fetchData])

return <ChildComponent fetchData={fetchData} />

Hope it helps to learn few different variant of mistakes while fetching data in useEffect.

Keep learning and share your comments by tagging me on twitter @learnwithparam 🙌

Beginners to ProNode Js

Visual Guide to API Design Best Practices

This visual eBook covers essential best practices for designing robust APIs using REST principles.

This book is ideal for beginners and backend developers seeking to enhance their API design skills. However, it is not suited for those seeking an in-depth exploration of API design. This book is a quick read under 40 slides like scrolling through your instagram feed.

Visual Guide to API Design Best Practices