Fetching data from backend causes infinite loop and never successfully loads

Viewed 90

This is probably my inexperience with Async functions showing here but i'm having an issue where my code to fetch templates from a back-end into react simply repeats without properly setting the state to the data

import React, { useState} from 'react';
import './main.css';
import Frame from './Components/Frame';


function App() {
    const [view, setView] = useState(window.location.pathname);

    console.log('logging view ' + view);

    return (
        <div className="App container m-4">
            <Frame viewF={'nav'} />
            <div id='content'>
                <Frame viewF={view} />
            </div>
        </div>
    );

}

export default App;

main component

import React, { useState } from 'react';

function createMarkup(markup) {
    return { __html: markup };
}

const Frame = (props) => {
    const [content, setContent] = useState('');

    const contactBackend = async () => {
        try {
            const response = await fetch('http://localhost:5000/' + props.viewF, {
                'methods': 'GET',
                headers: {
                    'Content-Type': 'application/json'
                }
            })
            if (!response.ok) {
                throw Error(response.statusText);
            }
            //const data = response.json(); //alternate approach
            //console.log(data);
            setContent(response.json());

        } catch (error) {
            console.log(error);
        }
    }

    contactBackend();

    return (
        <div className="m-2" dangerouslySetInnerHTML={createMarkup(content)}>

        </div>
    )

};

export default Frame;

The backend can send data, it worked in an earlier and simpler version. I can also see the returned values with some console logs. but the most it ever puts into the returned div is [object Promise] and then it sends 1000s of requests until the back-end crashes

1 Answers

The code is unconditionally calling contactBackend in the function body, and the result is an enqueued state update which triggers a component rerender. This is an unintentional side-effect.

Typically this is called in an useEffect hook when mounting.

response.json() also returns a Promise, so you also want to wait for that to resolve.

Example:

useEffect(() => {
  const contactBackend = async () => {
    try {
      const response = await fetch('http://localhost:5000/' + props.viewF, 
        {
          'methods': 'GET',
          headers: {
          'Content-Type': 'application/json'
          }
        }
      );

      if (!response.ok) {
        throw Error(response.statusText);
      }

      const content = await response.json();
      setContent(content);
    } catch (error) {
      console.log(error);
    }
  }

  contactBackend();
}, []); // <-- empty dependency to run once after initial render
Related