React Best Practise for handling config files and iterative api-calls

Viewed 68

I have a two-layer application.

The API-App offers the bare data as JSON whereas the React-App is only used for presentation.

To get those data from the API, I'm want to load and static config.json in the public folder and fetch the data from the given URL or the default fallback, called example.json.

#config.json
{
    "api_url": "example.json"
    ...
}

By running the react-App, I want to do the following:

  1. Load the config.json once
  2. Save the api_url as State
  3. Initially load the data from the API by using the api_url-state
  4. Iterate every delay (e.g. 4secs) -> reload API data and refresh the presented content
function App()
  const [delay] = useState(4000);
  const [content, setContents] = useState<Array<Content>>([]);
  const [apiurl, setApiurl] = useState("");

  fetch(window.location.href + "config.json")
    .then((respone) => respone.json())
    .then((e) => setApiurl(e.API_URL));

  useEffect( () => {
    fetch(apiurl)
      .then((response) => response.json())
      .then((element) => setContent(element));
  }, [apiurl])

  useEffect(() => {
    document.title = "Confview-Frontend";

    function tick() {
      fetch(apiurl)
        .then((response) => response.json())
        .then((element) => setContent(element));
    }

    const intervalId = setInterval(tick, delay);
    return () => clearInterval(intervalId);
  }, [delay, apiurl]);

Unfortunately, the given code creates some sort of an infinity loop.

What am I missing to get the described behavior?

1 Answers

The first fetch is an unintentional side-effect called each time the component renders.

By running the react-App, I want to do the following:

  1. Load the config.json once
  2. Save the api_url as State
  3. Initially load the data from the API by using the api_url-state
  4. Iterate every delay (e.g. 4secs) -> reload API data and refresh the presented content

I think you just need to move the initial "(1) Load the config.json once" into a mounting useEffect hook, and only "(3) Initially load the data from the API by using the api_url-state" when the API URL updates and is valid. You can combine the two useEffect hooks you had as they basically both called the "tick" function, just call tick immediately.

// declare delay external to remove as dependency
const delay = 4000;

function App() {
  const [content, setContents] = useState([]);
  const [apiurl, setApiurl] = useState("");

  useEffect(() => {
    fetch(window.location.href + "config.json")
      .then((respone) => respone.json())
      .then((e) => {
        setApiurl(e.API_URL);
      });
  }, []);

  useEffect(() => {
    document.title = "Confview-Frontend";

    function tick() {
      if (apiurl) {
        fetch(apiurl)
          .then((response) => response.json())
          .then((element) => setContents(element));
      }
    }

    // setup the interval
    const intervalId = setInterval(tick, delay);
    // initial tick now/immediate
    tick();

    return () => clearInterval(intervalId);
  }, [apiurl]);

  ...
}

Edit react-best-practise-for-handling-config-files-and-iterative-api-calls

Related