Reputation: 11
in this code whenever I try to direct to /dashboard it wouldn't wait for the response of axios and goes immediately to return part and it use loggedin
with it's default value which is undefined
here. Well I guess I should use promises but I don't know how...
So I would appreciate it if someone could help.
import axios from 'axios';
import React, { useEffect, useRef, useState } from 'react';
import { Redirect } from 'react-router';
import OverallCondition from "./dashOverall";
import Toolbar from "./dashToolbar";
export default function Dashboard(){
const [loggedin, check] = useState()
axios.get('/loggedin')
.then(response => {
check(response.data)
})
.catch(err => console.error(err));
return <section className='dashboard'>
{loggedin ? <div>
<Toolbar/>
<OverallCondition/>
</div> : <Redirect to='/login'/>}
</section>
}```
Upvotes: 0
Views: 1471
Reputation: 29282
You need to use useEffect
hook to make the HTTP request.
Making the HTTP request at the top-level inside your function component will trigger a new HTTP request every time your component is re-rendered.
Also, since the axios.get(...)
is asynchronous, code below the axios.get(...)
will execute before the request is completed.
To handle this situation appropriately, follow the steps mentioned below:
Create a state that represents whether the HTTP request is pending or not. Its initial value should be true
const [isPending, setIsPending] = useState(true);
Use the useEffect
hook to make the HTTP request. Making HTTP requests at the top-level inside the function component is NOT the right approach - you don't want to make a request every time your component re-renders
Also don't forget to set isPending
to false
, otherwise user will continue to see the loading spinner even after the request has completed. You can use the finally()
method to call setIsPending(false)
useEffect(() => {
axios.get('/loggedin')
.then(response => setLoggedIn(response.data))
.catch(err => console.error(err))
.finally(() => setIsPending(false));
}, []);
Empty array passes as a second argument to the useEffect
hook will ensure that the HTTP request is initiated only once, after the initial render of the component
While the request is pending, show some loading spinner to the user. When the component first renders, as the isPending
is true, user will see the loading spinner
if (isPending) {
return <Spinner/>; // you need to create the "Spinner" component
}
Here's how you could implement the above steps in your code:
function Dashboard() {
const [loggedin, setLoggedIn] = useState();
// this state will be used to show a loading spinner
const [isPending, setIsPending] = useState(true);
// for HTTP request, use the "useEffect" hook
useEffect(() => {
axios.get('/loggedin')
.then(response => setLoggedIn(response.data))
.catch(err => console.error(err))
.finally(() => setIsPending(false));
}, []);
// show a spinner to the user while HTTP
// request is pending
if (isPending) {
return <Spinner/>;
}
return (
<section className='dashboard'>
{loggedin ? (
<div>
<Toolbar/>
<OverallCondition/>
</div>
) : <Redirect to='/login'/>
}
</section>
);
}
Upvotes: 1
Reputation: 994
Issues with the code
Instead of
const [loggedin, check] = useState()
Use
const [loggedin, setLoggedin] = useState()
Your axios.get is inside the function body, all code there will be executed on every render, it is certainly not what you wanted, for operations that may have side effects, you need to use the useEffect hook https://reactjs.org/docs/hooks-effect.html
The useEffect hook allows you to control better when the code will execute, in your case, you want it to run once
const ShowOnceLoaded = ({ isLoaded, children }) => {
if (!isLoaded)
return <p>Loading...</p>
return <>{children}</>
}
export default function Dashboard(){
const [ isLoaded, setIsLoaded ] = useState(false)
const [loggedin, setLoggedin] = useState(false)
React.useEffect(() => {
axios.get('/loggedin')
.then(response => {
setLoggedin(true)
setIsLoaded(true)
})
.catch(err => console.error(err));
}, [])
return <section className='dashboard'>
<ShowOnceLoaded isLoaded={isLoaded}>
{loggedin ? <div>
<Toolbar/>
<OverallCondition/>
</div> : <Redirect to='/login'/>}
</ShowOnceLoaded>
</section>
}
In addition to what you had, now there is a state set once the request is complete, only then we can decide if the user is logged in or not, otherwise, we will redirect the user even before the request is done, as the state initializes as false by default
The empty array on useEffect is used to run the code only when the component mounts
Upvotes: 0