Next js routing triggers useEffect - reactjs

I'm trying to fetch data inside useEffect using axios but it fetches the data everytime I change the page. Is there a way to not trigger useEffect when changing the page? I tried to use shawllow push but it didn't work. I can't fetch on server side because I use next/router inside axios interceptor.
import { useRouter } from 'next/router';
const router = useRouter();
router.push('/', '/', { shallow: true })
With the above code, it will trigger useEffect when switching routes.
useEffect(() => {
fetch.get('/endpoint').then(response => {
// do stuff
});
}, []);

If you have something in useEffect that you only want to run once, add an empty array as the dependency array.
Example:
useEffect(() => {
// your axios fetch here
}, []);
Empty array as the dependencies means the effect is run once. If you don't specify the dependencies, the effect will run after every render.

Try:
useEffect(() => {
fetch.get('/endpoint').then(response => {
// do stuff
});
}, []); // the empty array will call useEffect only for first time while loading the component
Refer: https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects See Notes

Shallow-routing only works for same page. See this.
If you stick to shallow-routing, you have to aggregate the pages into one page with dynamic-routes.
So I think it'd be better to use some cache strategy within useEffect.

Related

React useState hook not updating with axios call

I am aware this is a common question as I have spent the last two hours going through every answer and trying to get my state to update but nothing is working.
I am fetching text from a cms however on first load the state is undefined and my app crashes. However if I comment the line out, load the page and uncomment the line the correct values are displayed.
Here is some of the code I have tried.
The data i am hoping to get
[
{id:1},
{id:2},
{id:3},
{id:4},
]
import react, {useEffect, useState} from 'react'
import axios from 'axios'
const [carouselTitle, setCarouselTitle] = useState([])
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
await axios('api').then(
response => {
console.log(response.data)
setCarouselTitle(response.data)
console.log(carouselTitle)
})
};
return(
<h1>{carouselTitle[1].id}</h1>
)
console logging the data works fine but when i console log the state it does not work.
2ND METHOD I TRIED
useEffect(() => {
const fetchData = async () => {
const res = await axios('api');
const carouselTitleAlt = await res.data;
setCarouselTitle({ carouselTitleAlt });
console.log(carouselTitleAlt);
console.log(carouselTitle);
};
fetchData();
}, []);
Again console logging the const inside the useEffect displays the correct information but logging the state does not work.
Appreciate your responses or better ways of displaying the data.
setState is asynchronous : https://reactjs.org/docs/faq-state.html#why-doesnt-react-update-thisstate-synchronously
It means that you cannot expect to console.log the new state value the line after you called setCarouselTitle.
To log the new value, you could use another useEffect, with carouselTitle in the dependencies array, where you console.log(carouselTitle) :
useEffect(() => {
console.log(carouselTitle);
}, [carouselTitle]);
That said, your component should behave correctly, it will be refreshed when the state is updated.
In the JSX you should check that carouselTitle is not undefined (meaning that the request failed or is still pending) :
{carouselTitle && <H1>{carouselTitle[0].id}}
https://reactjs.org/docs/conditional-rendering.html#gatsby-focus-wrapper
First of all, if you pass an empty array for initial data to useState, you can't get any item in that array in here:
return(
<h1>{carouselTitle[1].id}</h1>
)
Because component returns first item of an array that has nothing. I prefer to you do it like this:
return(
<h1>{carouselTitle.length > 0 && carouselTitle[0].id}</h1>
)
And also based on this and official documentation, setState (and also setSomthing() using useState()) is asynchronous.
So state data doesn't show immediately after setting that.
You should trigger useEffect for run fetch function
useEffect(()=>{fetchData();},[carouselTitle])

React useEffect and setInterval

I'm making a React dashboard that calls an API every minute for updates. Following the many answers in SO, I have this at the moment that sort of works:
const Dashboard = (props) => {
const [stockData, setStockData] = useState([]);
useEffect(() => {
//running the api call on first render/refresh
getAPIData();
//running the api call every one minute
const interval = setInterval(() => {
getAPIData()
}, 60000);
return () => clearInterval(interval);
}, []);
//the api data call
const getAPIData = async () => {
try {
const stdata = await DataService.getStockData();
setStockData(stdata);
}
catch (err) {
console.log(err);
}
};
However I keep getting browser warning
React Hook useEffect has a missing dependency: 'getAPIData'. Either include it or remove the dependency array
Is this a cause for concern (e.g. causing memory leaks)?
Ive tried to fix it:
It doesnt seem possible to not use useEffect (use setInterval directly)
If I remove dependency or put stockData as dependency for useEffect,
I'll see the API call being made every second, so I assume thats not
right.
If I put the api data call block directly in useEffect, the page wont
have the data shown when it loads the first time/or the page refreshed and I have to wait
for a minute.
I found several references on the issue such as
here
and
here
but I couldn't comprehend it given that I've only started using React
a month ago.
Appreciate any help for this!
You can resolve this issue in multiple way:
You can put getApiData in useEffect direct and use it...
You can use useCallBack, useEffect is go to re-render and make mempry leeek issue since every time react render Dashboard its re-create the getAPIData, you can prevent this case by using useCallBack, and you must make sure about dependency, just you need to put what you need...for example:

i want to send request to api when the page is loaded

I have an api and I want to request to api when the page loads. I use useEffect, but useEffect always makes requests and I don't want it. How can I make it possible to request only 1 time when the page loads?
My code
useEffect(() => {
fetch("API_URL")
.then(response=>response.json())
.then(data=>setDatas(data.reverse()))
})
Try this
useEffect(() => {
fetch("API_URL")
.then(response=>response.json())
.then(data=>setDatas(data.reverse()))
}, [])
The [] at the end defines the dependency for the useEffect hook. So an empty array defines no dependency and thus will trigger only once the component/screen loads.

Infinite re-render in functional react component

I am trying to set the state of a variable "workspace", but when I console log the data I get an infinite loop. I am calling the axios "get" function inside of useEffect(), and console logging outside of this loop, so I don't know what is triggering all the re-renders. I have not found an answer to my specific problem in this question. Here's my code:
function WorkspaceDynamic({ match }) {
const [proposals, setProposals] = useState([{}]);
useEffect(() => {
getItems();
});
const getItems = async () => {
const proposalsList = await axios.get(
"http://localhost:5000/api/proposals"
);
setProposals(proposalsList.data);
};
const [workspace, setWorkspace] = useState({});
function findWorkspace() {
proposals.map((workspace) => {
if (workspace._id === match.params.id) {
setWorkspace(workspace);
}
});
}
Does anyone see what might be causing the re-render? Thanks!
The effect hook runs every render cycle, and one without a dependency array will execute its callback every render cycle. If the effect callback updates state, i.e. proposals, then another render cycle is enqueued, thus creating render looping.
If you want to only run effect once when the component mounts then use an empty dependency array.
useEffect(() => {
getItems();
}, []);
If you want it to only run at certain time, like if the match param updates, then include a dependency in the array.
useEffect(() => {
getItems();
}, [match]);
Your use of useEffect is not correct. If you do not include a dependency array, it gets called every time the component renders. As a result your useEffect is called which causes setProposals then it again causes useEffect to run and so on
try this
useEffect(() => {
getItems();
} , []); // an empty array means it will be called once only
I think it's the following: useEffect should have a second param [] to make sure it's executed only once. that is:
useEffect(() => {
getItems();
}, []);
otherwise setProposal will modify the state which will trigger a re-render, which will call useEffect, which will make the async call, which will setProposal, ...

React useEffect hook missing dependencies linter warnings

I am using the React useEffect hook to obtain API data on component load, with the useAxios hook. The code is as below (simplified):
const [formData, setFormData] = useState<FormData>();
const [{ , executeGet] = useAxios('', {
manual: true,
});
const getFormData = async () => {
let r = await executeGet({ url: `http://blahblahblah/`});
return r.data;
};
useEffect(() => {
const getData = async () => {
try {
let response = await getAPIData();
if (response) {
setFormData(response);
} catch (e) {
setFormError(true);
}
};
getData();
}, []);
This pattern is used frequently in the codebase, but I am getting the linter warning:
React Hook useEffect has missing dependencies: 'getFormData'. Either include them or remove the dependency array react-hooks/exhaustive-deps
I can suppress the warning successfully with:
// eslint-disable-line react-hooks/exhaustive-deps
but it feels wrong to do this!
I can add constants to the dependency list without a problem, however when I add the getFormData function, I get an infinite loop. I have read around the area a lot and understand why the dependencies are needed. I am not sure if the useEffect hook is the best way to obtain the data, or whether there is a way to fetch data.
The problem is that you are defining getFormData within the component. In each render, it is reassigned. As is, this would mean that your initial useEffect would only be bound to to first getFormData, not the one from the most recent render. This causes a warning because often this is not what you intend, particularly if your getFormData depended on state or props that could change.
The simplest solution in this case is to move the definition of your getFormData outside of your component, and use Axios directly instead of using a hook. That way it wouldn't need to be defined on every render anyways.
you should initiate getFormData function using useCallback hook and then put it in useEffect dependency list.
const getFormData = useCallback(async () => {
let r = await executeGet({ url: `http://blahblahblah/`});
return r.data;
}, [executeGet]);
you can read more about useCallback in reactjs site:
https://reactjs.org/docs/hooks-reference.html#usecallback

Resources