ricky11
ricky11

Reputation: 68

Too many re-renders React error while fetching data from API

I am building a simple recipe app and I have a problem with fetching my data from the API, because the code seems to run on every render and I do not even understand why it re-runs since I found that if I add the dependency array, it should run only once, right?

App.js

function App() {
  const [recipesList, setRecipesList] = useState([]);
  let [scroll, setScroll] = useState(0)



console.log(recipesList,"list");

  return (
    <div className="App">
      <a href="index.html"><img className="logo" src={logo} alt="Logo"/></a>
      <Recipes recipesList={recipesList}  getRecipes={setRecipesList} />
    </div>
  );
}

export default App;

Recipes.js

import React, {useEffect, useState} from "react";
import Recipe from "../Recipe/Recipe";
import "./Recipes.css";

const Recipes = (props) => {

useEffect( () => {
    if (props.recipesList.length === 0) {
        fetch("myapi.com/blablabla")
        .then(res => res.json())
        .then(result => {
            props.getRecipes(result.recipes);
            }
        )
    }
    else {
        console.log("Do not fetch");
    }
    return () => console.log("unmounting");
        
}, [props])




const recipeComponent = props.recipesList.map( (item) => {
        return <Recipe className="recipe" info={item}/>
    })
    return(
        <div className="recipes">
            {recipeComponent}
            <h1>Hello</h1>
        </div>
    )
}


export default Recipes;

Upvotes: 1

Views: 1466

Answers (3)

H9ee
H9ee

Reputation: 420

try this code :

function App() {
  const [recipesList, setRecipesList] = useState([]);
  let [scroll, setScroll] = useState(0)

const getListPropd = (e) => {
setRecipesList(e)
}
console.log(recipesList,"list");

  return (
    <div className="App">
      <a href="index.html"><img className="logo" src={logo} alt="Logo"/></a>
      <Recipes recipesList={(e) => getListPropd (e)}  getRecipes={setRecipesList} />
    </div>
  );
}

export default App;
const [checkData , setCheckData ] = useState(true)

useEffect( () => {
    if (checkData) {
        fetch("myapi.com/blablabla")
        .then(res => res.json())
        .then(result => {
            props.recipesList(result.recipes);
            }
        if(props.recipesList.length > 0) {
          setCheckData(false)
         }
        )
   
    else {
        console.log("Do not fetch");
    }


    return () => console.log("unmounting");
        
}, [checkData])

Upvotes: 1

Emiel Zuurbier
Emiel Zuurbier

Reputation: 20944

Components will re-render every time your the props or state changes inside of the component.

I would recommend keeping the fetching logic inside of the Recipes component, because A: its recipe related data, not app related data. And B: this way you can control the state in Recipes instead of the props. This will give you more control on how the component behaves instead of being dependent on the parent component.

In the useEffect hook, leave the dependency array empty. This will cause the component to render, call useEffect only the first time, load your data and then render the recipes without re-rendering further.

import React, { useEffect, useState } from "react";
import Recipe from "../Recipe/Recipe";
import "./Recipes.css";

const Recipes = () => {
  const [recipesList, setRecipesList] = useState([]);

  useEffect(() => {
    fetch("myapi.com/blablabla")
      .then((res) => res.json())
      .then((result) => {
        setRecipesList(result.recipes);
      });

    return () => console.log("unmounting");
  }, []);

  // On the first render recipeComponents will be empty.
  const recipeComponents = recipesList.map((item) => <Recipe className="recipe" info={item}/>)

  return (
    <div className="recipes">
      {recipeComponents}
      <h1>Hello</h1>
    </div>
  );
};

export default Recipes;

Upvotes: 1

ioannis.th
ioannis.th

Reputation: 453

the useEffect hook uses an empty dependency array, [] if it should ONLY run once after component is mounted. This is the equivalent of the old lifecycle method componentDidMount()

If you add a non-empty dependency array, then the component rerenders EVERY time this changes. In this case, every time your component receives new props (i.e. from a parent component, this triggers a reload.

see more info here https://reactjs.org/docs/hooks-effect.html , especially the yellow block at the bottom of the page

Happy coding!

Upvotes: 0

Related Questions