React Hooks infinite loop Memory leak? - reactjs

Hello i have a problem with infinite loops. All the exampels i look at makes the loop go away. But then it wont update my view if i dont refresh. I want the items to appear when something gets added. i thought my example would work but it does not. I dont know how to fix it
const [ReadBookingTimes, SetBookingTimes] = useState([]);
const [readStatus, setStatus] = useState("");
const getBookingTimes = async () => {
try {
const response = await Axios.get(`${API_ENDPOINT}`);
console.log(response);
// debugger;
SetBookingTimes(response.data);
// setStatus("Connection sucsessfull");
} catch (err) {
setStatus("Error getting BookingTimes");
}
};
//reupdate State and prevent infinite loop
useEffect(() => {
getBookingTimes(ReadBookingTimes);
}, [ReadBookingTimes]); //);

Your useEffect has a dependancy ReadBookingTimes which updates every time. Bascially, you update the ReadBookingTimes every single time over and over again.
What you should do is to add a condition to check if your data is already loaded and if so, don't call the function:
const [ReadBookingTimes, SetBookingTimes] = useState([]);
const [readStatus, setStatus] = useState("");
const [dataLoaded, setDataLoaded] = useState(false);
const getBookingTimes = async () => {
// Your fetch code
setDataLoaded(true)
}
useEffect(() => {
// don't go any further of data is already loaded.
if (dataLoaded) return;
getBookingTimes(ReadBookingTimes);
}, [ReadBookingTimes, dataLoaded]);
Now you won't get stuck in a loop. And anytime you want to reload the data, you can change the dataLoaded to false and useEffect will call the getBookingTimes function again.
In case you want to get the data every 2 minutes for example, you can add a timeout to change the dataLoaded state every time causing another data reload.

Related

How can I stop react from making multiple network request simultaneously

I am making a get request to get data from my rest API, and when I have the data react will keep on making the same request simultaneously multiple times.
this is the code:
export default function Transaction() {
const [transactions, setTransaction] = useState([]);
const [loading, setLoading] = useState(true)
const { id } = useParams();
// this is where I am getting the problem
useEffect(() => {
const fetchTransc = async () => {
const res = await axios.get(`http://localhost:4000/api/get-records/${id}`);
setTransaction(res.data)
setLoading(false)
console.log(res.data)
};
fetchTransc();
},[id,transactions]);
The second argument of the UseEffect hook is the dependency array. It tells react which variables to watch for changes. When a dependency changes or when the component is mounted for the first time, the code inside the hook is executed.
In your case the array indicates that each time “id” or “transations” change, the hook should be executed.
Since setTransation is called in the fetch function, that will trigger the hook again because “transations” is present in hook’s the dependency array.
Each time the transactions state variable is set with a brand a new object fetched from the url, react will trigger the useEffect hook.
If “transations” is removed from the hook’s dependency array, this should work fine. Maybe also adding an IF to check the “id” value could be useful to prevent errors.
useEffect(() => {
const fetchTransc = async () => {
if(id != null) {
const res = await axios.get(`http://localhost:4000/api/get-records/${id}`);
setTransaction(res.data)
setLoading(false)
console.log(res.data)
}
};
fetchTransc();
},[id]);

ReactJS delay update in useState from axios response

I am new to react js and I am having a hard time figuring out how to prevent delay updating of use state from axios response
Here's my code:
First, I declared countUsername as useState
const [countUsername, setUsername] = useState(0);
Second, I created arrow function checking if the username is still available
const checkUser = () => {
RestaurantDataService.checkUsername(user.username)
.then(response => {
setUsername(response.data.length);
})
.catch(e => {
console.log(e);
})
}
So, every time I check the value of countUsername, it has delay like if I trigger the button and run checkUser(), the latest response.data.length won't save.
Scenario if I console.log() countUseranme
I entered username1(not available), the value of countUsername is still 0 because it has default value of 0 then when I trigger the function once again, then that will just be the time that the value will be replaced.
const saveUser = () => {
checkUser();
console.log(countUsername);
}
Is there anything that I have forgot to consider? Thank you
usually there is a delay for every api call, so for that you can consider an state like below:
const [loading,toggleLoading] = useState(false)
beside that you can change arrow function to be async like below:
const checking = async ()=>{
toggleLoading(true);
const res = await RestaurantDataService.checkUsername(user.username);
setUsername(response.data.length);
toggleLoading(false);
}
in the above function you can toggle loading state for spceifing checking state and disable button during that or shwoing spinner in it:
<button onClick={checking } disabled={loading}>Go
i hope this help
.then is not synchronous, it's more of a callback and will get called later when the api finishes. So your console log actually goes first most of the time before the state actually saves. That's not really something you control.
You can do an async / await and return the data if you need to use it right away before the state changes. And I believe the way state works is that it happens after the execution:
"State Updates May Be Asynchronous" so you can't really control when to use it because you can't make it wait.
In my experience you use the data right away from the service and update the state or create a useEffect, i.g., useEffect(() => {}, [user]), to update the page with state.
const checkUser = async () => {
try {
return await RestaurantDataService.checkUsername(user.username);
} catch(e) {
console.log(e);
}
}
const saveUser = async () => {
const user = await checkUser();
// do whatever you want with user
console.log(user);
}

React hooks with AJAX requests

I am using React and I was wondering if I am doing things correctly.
I want to make multiple ajax requests in React for multiple datalists for inputboxes.
I do something like this:
This is my fetch function
function GetData(url, processFunc) {
...
jquery ajax request using url
...
if (status === 'success') {
if (processFunc) {
processFunc(data);
}
}
}
A solution which only displays the results that were the fastest.
function ComponentA() {
const [ajaxDataA, setAjaxDataA] = useState(null);
const [ajaxDataB, setAjaxDataB] = useState(null);
const [ajaxDataC, setAjaxDataC] = useState(null);
const [dataA, setDataA] = useState(null);
...dataB..
...dataC..
const exampleFunctionA = function(data) {
..processes data into result
setDataA(result);
}
const exampleFunctionB = ....
const exampleFunctionC = ...
useEffect( () => {
GetData(url_1, exampleFunctionA);
GetData(url_2, exampleFunctionB);
GetData(url_3, exampleFunctionC);
}, []);
return (<>
...
{dataA}
{dataB}
...
<>);
}
B Solution is why Im asking this question. This works fine but I'm not sure it is correct or this is how hooks were meant to use.
function ComponentA() {
const [ajaxDataA, setAjaxDataA] = useState(null);
const [ajaxDataB, setAjaxDataB] = useState(null);
const [ajaxDataC, setAjaxDataC] = useState(null);
const [dataA, setDataA] = useState(null);
...dataB..
...dataC..
useEffect( () => {
GetData(url_1, setAjaxDataA);
GetData(url_2, setAjaxDataB);
GetData(url_3, setAjaxDataC);
}, []);
useEffect( () => {
..processes data into result
setDataA(result);
}, [ajaxDataA]);
..useEffect ... ,[ajaxDataB] ...
... [ajaxDataC] ...
return (<>
...
{dataA}
{dataB}
...
<>);
}
I have found this solution so I dont repeat the logic. I dont use this solution because I have to process the data so basically instead of
const a = GetData(..., processFunc);
I'd use this solution. And so I would still need the useEffects that watch when it refreshes
const a = useFetch(...)
Ajax request won't display in react render function
so basically the question is:
Is solution B a good solution?
Okay well.. I leave the question up if anyone else had gone on this awful path. So basically I was waiting for the AJAX requests to finish loading with the useEffect hook
At first it looked pretty logical:
I send the request.
when it's done it calls the useState hooks' setter function
the state variable updates
useEffect hook triggers
and I do something with the arrived data in the useEffect body
This was major dumb dumb
since it refreshed the state like it was forced to, having a gun pointed at its head
It caused inconsistencies: A child component would want to use the AJAX data before it arrived. It was like a random number generator. Half the time the page loaded and the other half it died running into nullreference exceptions.
solution: Promise

Not awaiting for data in useEffect

I have a useEffect in my component that is waiting for data from the context so that it can set it in state. But its not waiting for the state and is moving on to the next line of code to set isLoading to false.
I'd like it to wait for the data so that I can render the loading.
I tried setting the isFetchingData in the context but I had run into problems where if another component calls it first it would set the isFetchingData state to false.
First call to ReactContext is setting the isLoading sate to false
It is fine for results to come back with no records. The component would render 'No records found'. Therefore, I cannot check the length on state to say if length is zero then keep loading.
Following is my code:
Context
const [activeEmployees, setActiveEmployees] = useState([]);
const [terminatedEmployees, setTerminatedEmployees] = useState([]);
useEffect(() => {
getEmployees()
.then(response => {
/// some code...
setActiveEmployees(response.activeEmployees)
setTerminatedEmployees(response.terminatedEmployees)
});
});
Component
const EmployeesTab = () => {
const { activeEmployees, terminatedEmployees } = useContext(BlipContext);
//Component states
const [isFetchingData, setIsFetchingData] = useState(true);
const [newEmployees, setNewEmployees] = useState([]);
const [oldEmployees, setOldEmployees] = useState([]);
useEffect(() => {
async function getData() {
await setNewEmployees(activeEmployees);
await setOldEmployees(terminatedEmployees);
setIsFetchingData(false);
}
getData();
}, [activeEmployees, terminatedEmployees, isFetchingData]);
if(isFetchingData) {
return <p>'Loading'</p>;
}
return (
// if data is loaded render this
);
};
export default EmployeesTab;
Since you have useState inside your useContext, I don't see the point of storing yet again the activeEmployees in another state.
If you want to have a local loading variable it could something like:
const loading = !(activeEmployees.length && terminatedEmployees.length);
This loading will update whenever getEmployees changes.
And to answer you question, the reason await is not having an effect is because setState is synchronous.

Updating Hooks state resulting in infinite loop

I tried to clone the state, update the clone then set the state to cloned value, but getting an error "Too many re-renders. React limits the number of renders to prevent an infinite loop."
Here's my code
const [poketypes,setPoketypes] = useState([])
useEffect(()=>{
axios.get("https://pokeapi.co/api/v2/type")
.then(response => {
setPoketypes(response.data.results);
})
},[])
const all = {name:"all",url:""};
const updatedPoketypes = [...poketypes];
updatedPoketypes.push(all);
setPoketypes(updatedPoketypes);
const App = () => {
const [poketypes,setPoketypes] = useState([])
useEffect(()=>{
axios.get("https://pokeapi.co/api/v2/type")
.then(response => {
setPoketypes(response.data.results);
})
},[])
const all = {name:"all",url:""};
const updatedPoketypes = [...poketypes];
updatedPoketypes.push(all);
setPoketypes(updatedPoketypes);
return somediv;
}
If you can see the body of this component, setPoketypes will be invoked and entire body of App will be re-render, and when it's re-render, setPoketypes will be invoked again, and re-render again, and hence you're getting the infinite loop.
Depending on what you need, you should really scope your setPoketypes in some certain event or function call.
UPDATES
I'm guessing you want to have a all being added to poketypes at the end of the array, if so do take a look at below code.
const App = () => {
const [poketypes,setPoketypes] = useState([])
useEffect(()=>{
axios.get("https://pokeapi.co/api/v2/type")
.then(response => {
const all = {name:"all",url:""};
setPoketypes([...response.data.results, all]);
})
},[])
return somediv;
}
Either you want to assign all as one of the default value of poketypes, or you can place it in the useEffect as shown above

Resources