Reputation: 68
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
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
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
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