Infinate loop with fetching data in UseEffect - reactjs

Having a weird issue where by i am fetching some data from my local api, and it is infinately calling it for some strange reason:
import React, { useState, useEffect } from 'react';
const Users = () => {
const [users, setUsers] = useState([])
const fetchUsers = async () => {
try {
await fetch('http://localhost:3001/users')
.then(response => response.json())
.then(data => setUsers(data));
}
catch(ex) {
console.error('ex:', ex);
}
}
useEffect(() => {
fetchUsers();
}, [users])
return <div>xxx</div>
}
export default Users;
If I console.log(data) instead of setUsers(data), then all seems to be fine and the console log only outputs 1 set of information.
I am unsure what I am doing wrong. Any ideas?

useEffect(() => {
fetchUsers();
}, [users])
should be:
useEffect(() => {
fetchUsers();
}, [])
The first will fetch users every time user changes. then it changes the users object with the results which causes the infinite loop.
The fix instead only calls it once on mount.

Your effect:
useEffect(() => {
fetchUsers();
}, [users]);
will be executed whenever someone changes users object. In this case, the first time you will call useEffect, and fetch data from API, when you receive data from the backend you will update users object and trigger an infinite loop.
You can solve problem with:
useEffect(() => { fetchUsers(); }, []);

Related

React- Issue with infinite loop in useEffect

I have a useEffect() that fetches the data through axios, I want that to render only one time so I passed an array. Everything works fine, but the problem is whenever I try to sort the items, the second useEffect fires just followed by the first useEffect, which is causing the component to fetch the items all over again and again.
const [products, setProducts] = useState([]);
useEffect(() => {
const getProducts = async () => {
return await axios
.get('/getAllProducts')
.then((response) => {
setProducts(response.data);
console.log(products);
})
.catch((e) => {
console.log(e);
});
};
getProducts();
}, [products]);
This is because you passed an array containing your products state, rather than an empty array, which will fire useEffect on state change (for products state specifically). Try changing your code to an empty array:
useEffect(() => {
const getProducts = async () => {
return await axios
.get('/getAllProducts')
.then((response) => {
setProducts(response.data);
console.log(products);
})
.catch((e) => {
console.log(e);
});
};
getProducts();
}, []);
As #skyboyer mentioned below, it is good to note that state is not updated in a synchronous manner. Therefor, console.log(products) will not reflect an accurate value for your state when useEffect runs.
It is okay to use multiple useEffect hooks. If you would like to view your updated state in the console, or do some other work with it, you could add another useEffect hook and pass your state into the array:
useEffect(() => {
console.log(products);
}, [products]);
Since products is in the useEffect dependency array, it is going to run every time there are changes made to the products state. getProducts() runs setProducts which then in turn is going to trigger the use effect again. Using an empty array in the useEffect will tell it to only run when the component is mounted.
Like this:
useEffect(() => {
const getProducts = async () => {
return await axios
.get('/getAllProducts')
.then((response) => {
setProducts(response.data);
console.log(products);
})
.catch((e) => {
console.log(e);
});
};
getProducts();
}, []);

React useEffect to run on both component mounting and dependency change

I want to know how to run the useEffect side effect in both component mounting and a dependent value change. Currently I'm using two useEffects to achieve this like this.
useEffect(() => {
let isMounted = true;
const getUsers = async () => {
try {
const userResponse = await api.get('/users');
if (isMounted) { setUsers(userResponse.data); }
} catch (error) {
console.log(error);
}
};
getUsers();
}, []);
useEffect(() => {
let isMounted = true;
const getUsers = async () => {
try {
const userResponse = await api.get('/users');
if (isMounted) { setUsers(userResponse.data); }
} catch (error) {
console.log(error);
}
};
getUsers();
}, [netInfo]);
Is there anyway to achieve this using one useEffect?
Runs when the component is mounted for the first time and on every re-render
useEffect(() => {})
Runs when the component is mounted for the first time alone
useEffect(() => {}, [])
Runs when the component is mounted for the first time and whenever the someDependency's value changes .
useEffect(() => {}, [someDependency])
You can remove the first useEffect .

useEffect infinite loop network request

I am getting infinite requests on my network, and it's due to my useEffect. I know that the problem is because I am putting in the brackets as the second argument the 'posts' and the 'setPost' inside the useEffect function, but I need the page to render whenever I add a new post, so the 'posts' must be inside the brackets.
function Home() {
const {userData, setUserData} = useContext(userContext)
const [posts, setPost] = useState([])
const [createPost, setCreatePost] = useState('')
const handleToken = () => {
localStorage.removeItem('auth-token')
}
const token = localStorage.getItem("auth-token");
const handleOnSubmit = (e) => {
e.preventDefault()
axios.post('http://localhost:5000/posts', {textOfThePost: createPost}, {
headers: { 'auth-token': token },
})
.then((res) => {setCreatePost("")})
axios.get('http://localhost:5000/posts')
.then(res => {
setPost(res.data)
})
}
useEffect(() => {
}, [posts])
If you're doing setPost inside useEffect, I assume posts being changed, and you've added posts as dependency in useEffect, Of course which will re-call and it goes infinite loop. Make sure when do you want to call posts API.
const [posts, setPost] = useState([])
useEffect(() => {
axios.get('http://localhost:5000/posts')
.then(res => {
setPost(res.data) // Which will change `posts`
})
}, [posts]) // this will trigger useEffect and It goes infinite loop
// Change it to
useEffect(() => {
axios.get('http://localhost:5000/posts')
.then(res => {
setPost(res.data) // Which will change `posts`
})
}, []) -> Which call only one time
This useEffects gets called everytime posts changes, and inside the useEffect you're changing posts value, so you got into an recursive loop.
useEffect(() => {
axios.get('http://localhost:5000/posts')
.then(res => {
setPost(res.data)
})
}, [posts])
If you want it to get called only once, you should leave the empty array in your effect, so it will only get called once when your component is mounted.
useEffect(() => {
axios.get('http://localhost:5000/posts')
.then(res => {
setPost(res.data)
})
}, [])

How can I make react's useEffect to stop rerender in an infinite loop even with a dependency specified?

I am working on a small CRUD fullstack app with react and mongodb and I have this problem where I use useEffect to make an axios get request to the server to get all of my todos. The problem is that useEffect does it's job but it also rerenders to infinity. This is my component:
export default function () {
...
const [todos, setTodos] = useState([]);
const currentUser = JSON.parse(localStorage.getItem('user'))._id;
useEffect(() => {
async function populateTodos () {
try {
const res = await axios.get(`http://localhost:8000/api/all-todos/${currentUser}`);
setTodos(res.data);
} catch (err) {
if (err.response) {
console.log(err.response.data);
console.log(err.response.status);
console.log(err.response.headers);
} else if (err.request) {
console.log(err.request);
} else {
console.log('Error: ', err.message);
}
}
}
populateTodos();
}, [todos]);
console.log(todos);
return (
...
);
}
So what I was expecting to happen is that that console.log to get printed only when the todos changes, like when I add a new todo and so on, but instead it gets printed forever.
You said that you need to fetch todos at first, and whenever todos change. I can suggest you a different approach, using one more variable, something like this:
const TodosComponent = (props) => {
const [todos, setTodos] = useState([]);
const [updatedTodos, setUpdatesTodos] = useState(true);
const fetchFunction = () => {
// In here you implement your fetch, in which you call setTodos().
}
// Called on mount to fetch your todos.
useEffect(() => {
fetchFunction();
}, []);
// Used to updated todos when they have been updated.
useEffect(() => {
if (updatedTodos) {
fetchFunction();
setUpdatesTodos(false);
}
}, [updatedTodos]);
// Finally, wherever you update your todos, you also write `updateTodos(true)`.
}

React Hook useEffect : fetch data using axios with async await .api calling continuous the same api

While I fetch data from API and set the response to a array using useEffect
it call the API repeat continuous.
let [product, setproduct] = useState([]);
async function fetchData() {
let response = await axios(
`api`
);
let user = await response.data;
setproduct(user);
console.log(product);
}
useEffect(() => {
fetchData();
});
From the docs,
Does useEffect run after every render? Yes! By default, it runs both after the first render and after every update. (We will later talk about how to customize this.) Instead of thinking in terms of “mounting” and “updating”, you might find it easier to think that effects happen “after render”. React guarantees the DOM has been updated by the time it runs the effects.
You can provide the empty dependency array / [] as second argument to useEffect, it is same as componentDidMount which will executes only once in component life cycle.
useEffect(() => {
fetchData();
}, []); //This will run only once
Pass empty [] as an second argument to useEffect method. It will be called on initial render, like below.
useEffect(() => {
fetchData();
}, []);
i think the blow example will help you through fetch API .
import React , {useEffect} from "react";
import axios from "axios";
const Courses = ()=>{
useEffect(()=>{
getProducts()
})
const getProducts = async() => {
await axios.get('api/get_all_products')
.then(({data}) =>{
console.log("this is data from api ");
console.log('data' , data);
} )
console.log("data ehre ");
}
return(
<div>
<h2>Products data here</h2>
</div>
)
};
export default Courses;
let [product, setProduct] = useState([]);
// This function will be only called once
async function fetchData() {
let response = await axios("api");
let user = response.data;// Don't need await here
setProduct(user);
console.log(product);
}
useEffect(() => {
fetchData();
}, []);

Resources