killerprince182
killerprince182

Reputation: 465

Why I am getting re-render errors on loading page?

I have created an app that consists of a loading screen. However, if the API is failed after 5 seconds, a failure message is to be conveyed. I am getting too-many re-renders error on the line which I have mentioned in the code below.

I have used setTimeout function to replace the message of failure if API fails after 5 seconds of loading the page. The rest of my app functionality is working fine.

My app code is: -

function App() {

  //Is website loaded for first time?
  const [loadingFirstTime, setLoadingFirstTime] = useState(true);
  //Has the  site  loading  failed? If yes, pass that to loading component
  const [loadingFailed, setLoadingFailed] = useState(false);

  //To be run first time the website is loaded
  useEffect(() => {
    getMovies()
        .then(res => setMoviesDetails(res))
        .then(() => setLoadingFirstTime(false)); 
  }, [])

  ................... some code here .............


  //If the details of api haven't been loaded or api loading failed
  if (Object.keys(moviesDetails).length === 0 && loadingFirstTime) {

    //------**Error occurs here after 5 seconds as per the console**-----------------
    
    //check for the same thing after 5 seconds, if initial data still has been loaded?
    setTimeout(() => {
      if (Object.keys(moviesDetails).length === 0 && loadingFirstTime) {
        setLoadingFailed(true);
      }
    }, 5000);

    return (
      <LoadingScreen status={loadingFailed} />
    )
  }

  return (
    <>
      ........ App components which are working fine .............
    </>
  );
}

Code for my loading component: -

function LoadingScreen({status}) {

    const [loadingText, setLoadingText] = useState("Loading the site...");

    //check if site loading failed and display appropiate message
    if (status) {
        setLoadingText("Website loading failed. Please reload or contact the administrator.");
    }

    return (
        <div className="loading-screen">
            <h1>{loadingText}</h1>
        </div>
    )
}

Upvotes: 0

Views: 65

Answers (1)

crls_b
crls_b

Reputation: 720

In React, you should avoid changing states when rendering, which is what you are doing in your LoadingScreen component when you are setting loadingText.

setLoadingText("Website loading failed. Please reload or contact the administrator.");

This is happening all the time you are passing a truthy value to status. That line is making LoadingScreen component to re-render itself again and again in an infinite loop.

Generally, it is better to implement this feature inside a useEffect function like this one:

function LoadingScreen({ status }) {
  const [loadingText, setLoadingText] = useState("Loading the site...");

  useEffect(() => {
    if (status) {
      const newLoadingText =
        "Website loading failed. Please reload or contact the administrator.";

      if (newLoadingText !== loadingText) {
        // This state setter provokes a re-render so set only if new value is
        // different than the actual value stored in state
        setLoadingText(newLoadingText);
      }
    }
  }, [loadingText, status]);

  return (
    <div className="loading-screen">
      <h1>{loadingText}</h1>
    </div>
  );
}

Upvotes: 1

Related Questions