Cool cow facts
Cool cow facts

Reputation: 37

axios get request infinitely looping

In my function getFact(), I make a GET and return the data. The issue that I'm having is that the request is looping infinitely instead of just once.

here's my code:

import React, {useState} from "react";
import axios from "axios";

function Facts() {

  const [fact, setFact] = useState("");
  const [source, setSource] = useState("");
  const [id, setID] = useState("");

  function getFact() {
    axios.get("https://exampleAPI.com/",  { crossdomain: true }).then(response => {
      setFact(response.data[0].fact);
      setSource(response.data[0].sources);
      setID(response.data[0].id);
    });
  }

  getFact();

  return (
    <div>
      <h1 class="text-light fw-light landing-text">{id}</h1>
      <p class="text-light">fact number</p>
      <hr class="text-light" />
      <h1 class="text-light fw-light landing-text">"{fact}"</h1>
      <a class="text-light text-decoration-none" href={source} target="_blank" rel="noopener noreferrer">source</a>
      <br />
      <br />
      <button class="btn btn-lg btn-outline-light" onClick={getFact}>
          Next Fact
      </button>
    </div>    
  )
}

export default Facts;

Any help is appreciated!

Upvotes: 0

Views: 51

Answers (2)

Ochuko
Ochuko

Reputation: 110

You have to use the useEffect hook to handle the API call. The reason why is calls infinitely is because you call getFact every time the component is rendered, and within getFact you making a state update. Every time the state is updated the component is re-rendered, which then call getFact again, causing the infinite loop.

Try something like this:

import React, {useState, useEffect} from "react";
import axios from "axios";

function Facts() {

  const [fact, setFact] = useState("");
  const [source, setSource] = useState("");
  const [id, setID] = useState("");
  
  function getFact() {
    axios.get("https://exampleAPI.com/",  { crossdomain: true }).then(response => {
      setFact(response.data[0].fact);
      setSource(response.data[0].sources);
      setID(response.data[0].id);
    });
  };

  useEffect(()=>{
    getFact();
  },[]);


  return (
    <div>
      <h1 class="text-light fw-light landing-text">{id}</h1>
      <p class="text-light">fact number</p>
      <hr class="text-light" />
      <h1 class="text-light fw-light landing-text">"{fact}"</h1>
      <a class="text-light text-decoration-none" href={source} target="_blank" rel="noopener noreferrer">source</a>
      <br />
      <br />
      <button class="btn btn-lg btn-outline-light" onClick={getFact}>
          Next Fact
      </button>
    </div>    
  )
}

export default Facts;

Notice how I called getFact within useEffect. It'll call getFact only when the component is first rendered. useEffect also accepts a list of dependencies, which will determine if what ever is specified within the function will run. But I passed an empty array as my dependencies. See here, for more information.

Upvotes: 1

Avinash Thakur
Avinash Thakur

Reputation: 2019

From what it appears, you want to run getFact when the component mounts i.e. once for a component's lifecycle. To accomplish it, use useEffect hook with empty dependency array.

import React, {useState, useEffect} from "react";
import axios from "axios";

function Facts() {

  const [fact, setFact] = useState("");
  const [source, setSource] = useState("");
  const [id, setID] = useState("");

  function getFact() {
    axios.get("https://exampleAPI.com/",  { crossdomain: true }).then(response => {
      setFact(response.data[0].fact);
      setSource(response.data[0].sources);
      setID(response.data[0].id);
    });
  }

  // call getFact in a useEffect hook
  useEffect(() => {
    getFact();
  }, []);

  return (
    <div>
      <h1 class="text-light fw-light landing-text">{id}</h1>
      <p class="text-light">fact number</p>
      <hr class="text-light" />
      <h1 class="text-light fw-light landing-text">"{fact}"</h1>
      <a class="text-light text-decoration-none" href={source} target="_blank" rel="noopener noreferrer">source</a>
      <br />
      <br />
      <button class="btn btn-lg btn-outline-light" onClick={getFact}>
          Next Fact
      </button>
    </div>    
  )
}

export default Facts;

In your code, you're calling getFact function directly in the body of Facts functional component which means getFact will be called on every render. Also, you're updating the state (setFact) in the getFact function and hence making the component re-render whenever getFact is called. This creates an infinite loop.

In the above solution, getFact function will only be called when component is mounted.

You can benefit from these resources:

Upvotes: 2

Related Questions