Reputation: 9407
I am building a project using react hooks. My useEffect
function looks as follows...
useEffect(() => {
(async function() {
const queryParams = await qs.parse(props.location.search, { ignoreQueryPrefix: true }).code;
if (queryParams != undefined) {
setCode(queryParams);
postLogin();
}
})();
});
The postLogin
function is just a GET
request to a server using the query parameters defined above. It look as so...
const postLogin = async () => {
await axios.get("http://XXX.XXX.X.XX:52805/user/auth", {
params: {
auth_code: code
}
}).then(result => {
setAuthTokens(result.data.credentials.access_token);
setLoggedIn(true);
}).catch(e => {
setIsError(true);
});
}
What happens is that the request will get called multiple times when I only want it to get called once.
As you can see in the screenshot above, out of the 5 requests, one of them is successful, the others fail, and I'm not sure what is causing it. Any ideas would be helpful.
Upvotes: 0
Views: 432
Reputation: 658
You might need to add an empty array ( []
) as the second parameter to your hook. It stops the hook from running multiple times.
The array is supposed to contain variables ( normally part of the component's state ) that the hook should watch and rerun if they change. The variables should be vital for whatever the hook does.
Let's say your hook is supposed to display number of subscribers to a youtube channel in a certain category - let's say the categories are A, B and C (hypothetically) and the user can switch between which category to view may be using a dropdown; then the array would contain the category variable such that whenever it changes, the hook runs to fetch the number of subscribers in that category. Having the array empty is a way of telling React that you have no dependencies and you are ok with what the hook does the first time it runs.
React compares the values in that array for changes on each render and if they don't change, the hook is skipped. If you have it empty, then no value will ever change which means the hook will run once and then be skipped on subsequent rerenders.
Upvotes: 4
Reputation: 2572
Your useEffect
hook runs on each render. Since you call setIsError
or setLoggedIn
after the request finishes, this will trigger another render, and another render, etc. Honestly I'm surprised there isn't an infinite loop. To fix this, try making it only attempt to log in when it hasn't attempted before (hasn't had an error and isn't logged in currently.) Like this:
useEffect(async () => {
if (isLoggedIn || isError) return; // Don't try again if a request has already succeeded or failed.
// Optionally use "isLoggedIn || isError || isLoading" to prevent multiple concurrent requests,
// which may cause further re-rendering.
const queryParams = await qs.parse(props.location.search, { ignoreQueryPrefix: true }).code;
if (queryParams != undefined) {
setCode(queryParams);
postLogin();
}
});
If you are sure that you only ever want it to run once (on the first render) give it an empty dependency array so it will never run again.
Upvotes: 1