useEffect is causing infinite loop when use state as dependency - reactjs

Here simply I am fetching data from mysql DB and storing it in state and in order to fetch this data:
const [orders, setOrders] = useState([]);
To fetch data I am using different functions and finally I am calling those functions using useEffect simple enough and so for everything is working perfectly but the problem comes whenever I use the state as dependency where I am storing data beacause if I dont do that then I have to manually refresh the page for latest changes and I have tried every given solution on stackoverflow but any of the solution didnt work so someone can please help me how can I use this state as dependencey without causing infinite loop:
const [orders, setOrders] = useState([]);
const loadData = async () => {
const response = await fetch("http://localhost/k-shop/load.php");
const result = await response.json();
setOrders(result);
};
const loadTotal = async () => {
const response = await fetch("http://localhost/k-shop/amount.php");
const result = await response.json();
setTotal(result);
};
useEffect(() => {
loadData();
loadTotal();
}, [orders]);
console.log(orders);

If you move the state into the useEffect dependency, you can then check if it is empty, and only set it when that check passes.
It will set the state once to populate and not pass the check again.
const [orders, setOrders] = useState([]);
const loadData = async () => {
const response = await fetch("http://localhost/k-shop/load.php");
const result = await response.json();
setOrders(result);
};
const loadTotal = async () => {
const response = await fetch("http://localhost/k-shop/amount.php");
const result = await response.json();
setTotal(result);
};
useEffect(() => {
if(orders.length === 0) {
loadData();
}
// you can do the same with checking loadTotal() state
}, [orders]);
console.log(orders);

Avoid ,non-primitive data types in dependencyArray ,
useEffect(() => {
loadTotal();
loadData();
}, [total, orders.length]);

every times you "setOrders" means you change the state,every times you change the state,means the "useEffect" will do again.that cause infinite loops.why not try useEffect(() => {loadData()}, [])?

Related

react page doesnt render when getting the data back from server

react won't work when rendering the page, but when i changed code in the vscode (added a line of console or comment one line out), the page is rendered. or when page is not rendered. when i hit refresh. i can see some of my content but it won't render. the usestate doesnt seem like to successfully save the value
const ParkDetail = () => {
const [parkId, setParkId] = useState('')
const [park, setpark] = useState('')
const [areas, setAreas] = useState([])
const [ridesName, setridesName] = useState([])
const [rides, setrides] = useState([])
let { id } = useParams()
console.log(id)
useEffect(() => {
async function getPark() {
try {
await setParkId(id)
const res = await axios.get(`/parks/details/${parkId}`)
console.log(res)
const park1 = await res.data.park
console.log(park1)
await setpark(park1)
console.log(park)
await setAreas(park1.serviceAnimalRelief)
// await setridesName(park1.topRides)
// console.log(ridesName)
} catch (e) {
console.log(e.message)
}
}
getPark()
}, [parkId])
}
I believe the problem is you using the state as parameters in your get requests. Since state setter functions do not return promises, using await on them is basically of no use. You should rather use the variables that you obtain from the get requests directly. For example, use id instead of parkId and so on.

How to modify data after waiting for it to be fetched react axios

I'm trying to get data from my API then modifying it,
I tried this:
const [reports, setReports] = useState([]);
const [workers, setWorkers] = useState([]);
const [newReports, setNewReports] = useState([]);
useEffect(() => {
(async () => {
await get_user_data().then((dataRes) => {
axios
.get('/api/reports/?my_business=' + dataRes.id)
.then((dataRes) => {
setReports(dataRes.data);
return dataRes.data[0].my_business;
})
.then((dataRes) => {
axios
.get('/api/workers/?my_business=' + dataRes)
.then((dataRes) => {
setWorkers(dataRes.data);
})
.then(() => {
setNewReports(fix_data());
});
});
});
})();
}, []);
I got the reports and the workers, but when I set newReports I only get them in the first render, if I refresh I lose the data in new reports, that's because setNewReports(fix_data()) don't wait for the reports and the workers to be fetched.
Here's my fix_data() function:
function fix_data() {
let w = {};
let r = {};
let rep = [];
reports.forEach((report) => (w[report.worker_id] = workers.find((worker) => worker.id === report.worker_id)));
reports.forEach(
(report) => (
(r = report), (r.worker_id = w[report.worker_id].first_name + ' ' + w[report.worker_id].last_name), rep.push(r)
),
);
return rep;
}
What's wrong with my useEffect function? I do call setNewReports(fix_data()) inside then so why it doesn't wait for the data to be fetched?
Thank you
const [reports, setReports] = useState([]);
const [workers, setWorkers] = useState([]);
const [newReports, setNewReports] = useState([])
const get_reports = async (dataRes) =>{
const Report_Res = await axios.get('/api/reports/?my_business=' + dataRes.id)
setReports(Res.data);
}
const get_worker_data = async (dataRes)=>{
const Worker_Res = await axios.get('/api/workers/?my_business=' + dataRes)
setWorkers(dataRes.data);
}
useEffect(()=>{
get_user_data()
},[])
useEffect(()=>{
get_reports(dataRes)
get_worker_data(dataRes)
},[dataRes])
useEffect(()=>{
setNewReports(fix_data())
},[reports, workers])
#alaa yahia It's better if you practice clean code. :)
1.Try to wrap up your each api call in separate function. It looks cleaner and easy to understand.
Call the functions in useEffect. Look at the 1st useEffect. they will populate the states of user Data. 2nd useEffect fetches the worker and report data which depends on DataRes. So add DataRes in dependency array of 2nd useEffect.
call the setNewReports(fix_data()) in 3nd useEffect. and it will make sure It fixes the data when reports and workers data are present. Look at the dependency array- [reports, workers] at the end of 3rd useeffect. That means This useEffect will run, when these [reports, workers] changes.
N.B- I wrote those codes without running. Forgive if any Typing mistake presents.
Here's the code that worked after small fixes on #black lotus's answer:
const [reports, setReports] = useState([]);
const [workers, setWorkers] = useState([]);
const [newReports, setNewReports] = useState([]);
const [dataRes, setDataRes]= useState([]);
const get_reports = async (dataRes) =>{
const Report_Res = await axios.get('/api/reports/?my_business=' + dataRes)
setReports(Report_Res.data);
}
const get_worker_data = async (dataRes)=>{
const Worker_Res = await axios.get('/api/workers/?my_business=' + dataRes)
setWorkers(Worker_Res.data);
}
const get_user = async (dataRes)=>{
const user_Res = await get_user_data()
setDataRes(user_Res);
}
useEffect(()=>{
get_user()
},[])
useEffect(()=>{
get_worker_data(dataRes)
get_reports(dataRes)
},[dataRes])
useEffect(()=>{
setNewReports(fix_data())
},[reports, workers])

useEffect not triggering but the template is being rendered somehow

I am getting too many re-renders while using react-hooks.
I am trying to fetch data from api by using a parameter in URL.
Here's the code:
export default function Details() {
const { title } = useParams();
const [loading, setLoading] = useState(true);
const [details, setDetails] = useState([]);
const [error, setError] = useState("");
function getDetails(keyword) {
if (keyword) {
setLoading(true);
fetch(
`API`
)
.then((res) => {
let result = res.data.results;
result = result.filter(function (result) {
return (result.title = keyword);
});
setDetails(result[0]);
setLoading(false);
console.log(details);
})
.catch((err) => {
setError(["Unable to fetch data"]);
setLoading(false);
});
}
}
getDetails(title)
return(
// template
)
now I think this is happening at the line where I call getDetails.
then I tried using useEffect to load the data only once after it is mounted,
useEffect(() => {
getDetails(title);
}, []);
It still is unable to fetch the data, as the getDetails function is never called.
How can I resolve this?
Edit:
Fixed one silly error.
Here's the codesandbox link:
Codesandbox
There are multiple issues with this, first you need to specify what you want to be notified about when the useEffect gets called again. You could do this by adding the variables you want within the array
useEffect(() => {
getDetails(title);
}, [
// Add what you want here
]);
The second issue you have is that you declared the detalis variable twice. Once using the set state here: const [details, setDetails] = useState([]);
The second time here:
const details = getDetails(title)
the code here has two obvious error beside the functionality problems you mentioned:
1 - you cannot declare two variables with same name using let or const; it will throw a SyntaxError
const [details, setDetails] = useState([]);
...
const details = getDetails(title)
2- getDetails function is written with a asynchronous mindset, and it will return nothing,
so details in const details = getDetails(title) will be set to undefined
Looks like your getDetails function has title param so I would add title and getDetails both in the dependency list of useEffects
Or place getDetails inside the useEffect
Here is your working code. You had multiple problems where res.data was undefined so you need to get res.results directly based on your response object
useEffect(() => {
function getDetails(keyword) {
if (keyword) {
setLoading(true);
fetch(
`https://api.jikan.moe/v3/search/anime?q=${keyword}&page=1&genre_exclude=0`
)
.then((res) => res.json())
.then((res) => {
console.log(res.results);
let result = res.results;
console.log(result);
result = result.filter(function (result) {
return (result.title = keyword);
});
setDetails(result[0]);
setLoading(false);
console.log(3);
})
.catch((err) => {
console.log(err);
setError(["Unable to fetch data"]);
setLoading(false);
});
}
}
console.log('calling getDetails')
getDetails(title);
}, [title]);
Note: tested in the code sandbox link provided in the question. Its working code.

setState never gets set in useEffect after API call responds with data

I'm trying to update state immediately after data comes in from the API. The data is coming in, I can see it using the console.log right below my API request. All of the data is right but for some reason, setState never sets in my hook. It just returns and empty array even after the console displays data.
const [experienceData, setExperienceData] = useState([]);
const { match = {} } = props;
useEffect(() => {
async function fetchData() {
if (Object.keys(match.params).length > 0) {
const response = await ApiService.getExperiences(match.params.experieneId);
console.log(response)
setExperienceData(response)
}
}
fetchData();
}, []);
I must be doing something wrong but I can't figure out what that is. Hoping someone on here has run into the same issue.
UPDATE: I just changed everything over the a class and duplicated the exact code on another file and ran into the exact same issue. The console updates with the data, but the setState on the line immediately after the data does not setState.
async componentDidMount() {
if (Object.keys(this.props.match.params).length > 0) {
const response = await ApiService.getExperiences(this.props.match.params.experieneId);
console.log(response[0])
this.setState({ experienceData: response[0], occurrenceData: response[0].occurrences });
}
}
You have to useSetState in a proper way, the issue is in the setExperienceData
const [experienceData, setExperienceData] = useState({response:""});
const { match = {} } = props;
useEffect(() => {
async function fetchData() {
if (Object.keys(props.match.params).length > 0) {
const response = await ApiService.getExperiences(match.params.experieneId);
console.log(response)
setExperienceData(experienceData => ({ ...experienceData, response: response }));
}
}
fetchData();
}, []);
return(<div>check {experienceData.response}</div>)
I see you left the dependency array empty. This tells React to run this effect only once: when the component first renders. If you want your useEffect to respect your state hook, put setExperienceData inside the dependency array
const [experienceData, setExperienceData] = useState([]);
const { match = {} } = props;
useEffect(() => {
fetchData();
}, [props.match.params]);
const async fetchData = () => {
if (Object.keys(match.params).length > 0) {
const response = await ApiService.getExperiences(match.params.experieneId);
console.log(response)
setExperienceData([...response])
}
}
Could you please try passing [match.params] as the second argument to your useEffect.

async fetch triggered three times

I use this in my react app to fetch data from my backend server:
React.useEffect(() => {
const fetchWidgets = async () => {
const response = await fetch("http://localhost:1202/data");
const responseData = await response.json();
setData(responseData);
console.log(responseData);
};
fetchWidgets();
});
It fetching data works fine, but the function seems to be triggered three times for some reason.
responseData is logged three times.
React.useEffect runs every time after component renders, unless you tell it not by defining a dependency array as its second argument; since you are setting a state inside its body which causes the comonent to re-render, you will see it happens multiple times. to fix the problem you may pass an empty array [] it will only run once after first render and acts like componentDidMount in class components. or add some dependency to run only if the dependencies change;
React.useEffect(() => {
const fetchWidgets = async () => {
const response = await fetch("http://localhost:1202/data");
const responseData = await response.json();
setData(responseData);
console.log(responseData);
};
fetchWidgets();
},[]);
Use Empty Brackets for the second parameter of useEffect.
React.useEffect(() => {
const fetchWidgets = async () => {
const response = await fetch("http://localhost:1202/data");
const responseData = await response.json();
setData(responseData);
console.log(responseData);
};
fetchWidgets();
},[]);
That will ensure the useEffect only runs once.

Resources