DEV Community

Cover image for How to not Burn Money like Pablo Escobar while Fetching Data from an API in React.js
Ethan Groene
Ethan Groene

Posted on • Originally published at javascript.plainenglish.io

How to not Burn Money like Pablo Escobar while Fetching Data from an API in React.js

Originally published to Medium for JavaScript in Plain English on November 10, 2023

As a developer, you have probably had the chance to use APIs at some point. Many of these APIs, as you may know, make money by charging users every time they fetch data. In React.js, there is one crucial concept you need to be familiar with before working with APIs, and that is the Infinite Loop, which you have likely encountered, even if you have a miniscule amount of experience in React.js.

As you have perhaps surmised, you never want to be fetching API data inside of an Infinite Loop. Let’s take a look, first, at how we could cause a fetch call to be made inside of an Infinite Loop, then at how we could prevent this from happening.


In the example below, I am fetching (pun not intended) information about various dogs stored in a JSON file that runs on json-server, a Node package that is used to simulate a database. I then want to set the state value of allDogs, an array of objects, to the dogs array of objects stored in the database:

/* In reality, it's probably best to define this in a separate file 
containing comparable functions */
const getAllDogs = () => {
  // Returns a promise
  return fetch("http://localhost:3000/dogs").then((response) => response.json());
};

export function FunctionalApp() {
  const [allDogs, setAllDogs] = useState([]);

  // Don't make the fetch call like this!!!
  getAllDogs().then(setAllDogs);

  // rest of component
}
Enter fullscreen mode Exit fullscreen mode

Remember, every time a component’s state value changes, the component is rendered. So, in the code above, the fetch call is made from scratch every time the component renders, which happens every time the fetch call is made. This nightmarish cycle will continue on forever if we don’t put a stop to it. Any guesses as to how we can do so?

If your mind jumped to the useEffect hook, then you have a brilliant one. There are, however, many great tools, such as Tanstack's React Query, that can make handling requests much easier, but I'll just cover the useEffect way in this article. Here's a look at that in action:

const getAllDogs = () => {
  // Returns a promise
  return fetch("http://localhost:3000/dogs").then((response) => response.json());
};

export function FunctionalApp() {
  const [allDogs, setAllDogs] = useState([]);

  /* Call the fetch function inside a useEffect callback to prevent an 
  Infinite Loop */
  useEffect(() => {
    getAllDogs().then(setAllDogs);
  }, []);

  // rest of component
}
Enter fullscreen mode Exit fullscreen mode

For those unfamiliar, a useEffect hook takes two arguments: a callback function, which contains the code for things you would like to happen, and a dependency array, which contains the values of things that would trigger the useEffect to be called again. In the case that the dependency array is empty, as it is above, the useEffect will be triggered only when the component is rendered for the first time, so by making the fetch call inside a useEffect hook, we are preventing a new fetch call from being made every time the component is rendered.

If the dependency-array argument is omitted, the useEffect behaves the same way as if it were empty, but it is not good practice to do so.

Now, can you think how a useEffect hook could translate to a class component? Let's have a look:

export class ClassApp extends Component {
  state = {
    allDogs: [],
    // rest of state
  };

  // Setter for allDogs:
  setAllDogs = (newValue) => {
    this.setState((prevState) => ({
      ...prevState,
      allDogs: newValue,
    }));
  };

  /* Using a lifecycle method like this prevents an infinite loop caused 
  by a fetch call in a class component */
  componentDidMount() {
    getAllDogs().then((dogs) => {
      this.setAllDogs(dogs);
    });
  }

  // rest of component
}
Enter fullscreen mode Exit fullscreen mode

Here, using the componentDidMount lifecycle method accomplishes the same thing as the useEffect hook with an empty dependency array, thus preventing an infinite loop in a class component.
To connect this all to the title of this article, imagine that you or the company you work for pays a few cents for every fetch call made to an API. If that is called, say, 300 times per second, the cost could add up quickly, and even more so if the application is deployed to users! This mistake of making a fetch call outside of the useEffect hook or a lifecycle method could easily cause thousands of dollars to go up in smoke.
Thank God I didn't have to learn this lesson the hard way; hopefully you will remember this article and avoid learning this lesson the hard way, too.


Thank you for reading! I hope you learned something. I would like to hear what you think, whether that's in a reaction of some kind, or in a comment. If you have any critiques of what I presented here, I'd also love to hear them. If you think someone could benefit from this article, please share it with them.

Top comments (0)