Reputation: 23
Well, saying that everything else was tested and is working fine, I made this PublicRoute
that sends a request to NodeJS
, but the function isAuthenticated
never awaits the response from back-end and always returns a promise
instead of true
or false
. I searched everywhere on internet but did not find a solution. I don't know how to make it await.
PublicRoute file:
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import { useAuth } from '../context/auth';
import api from '../services/api'; // Axios.
function PublicRoute( { component: Component, ...rest } ) {
const { authTokens } = useAuth();
async function isAuthenticated ( token ) {
if ( token === undefined ) return false;
const data = {
token
};
try {
const response = await api.post( '/cT', data );
if ( response.status === 200 ) {
console.log( 'Retorned true.' );
return true;
} else {
console.log( 'Retorned false.' );
return false;
}
} catch ( error ) {
console.log( 'Retorned false with error.' );
console.log( error );
return false;
};
}
const estaAutenticado = isAuthenticated( authTokens );
console.log( 'Is authenticated?' );
console.log( estaAutenticado ); // It was supposed to return true or false, but always returns a promise.
return (
<Route { ...rest }
render={
( props ) => ( estaAutenticado === true ) ?
( <Redirect to='/profile' /> ) :
( <Component { ...props } /> )
}
/>
);
}
export default PublicRoute;
This is my Routes file:
import React, { useState } from 'react';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import { AuthContext } from './context/auth';
// Pages:
import PublicRoute from './pages/PublicRoute';
import PrivateRoute from './pages/PrivateRoute';
import Admin from './pages/Admin';
import Logon from './pages/Logon';
import Register from './pages/Register';
import User from './pages/Register/User';
import Ong from './pages/Register/Ong';
import Profile from './pages/Profile';
import NewIncident from './pages/NewIncident';
export default function Routes( props ) {
const localStorageToken = localStorage.getItem( 'token' );
let existingTokens = undefined;
if ( localStorageToken !== 'undefined' ) {
existingTokens = JSON.parse( localStorage.getItem( 'token' ) );
}
const [ authTokens, setAuthTokens ] = useState( existingTokens );
const setTokens = ( token ) => {
localStorage.setItem( 'token', JSON.stringify( token ) );
setAuthTokens( token );
};
return (
<AuthContext.Provider value={{ authTokens, setAuthTokens: setTokens }}>
<BrowserRouter>
<Switch>
<PublicRoute exact path='/' component={ Logon } />
<PublicRoute exact path='/register' component={ Register } />
<PublicRoute exact path='/register/ong' component={ Ong } />
<PublicRoute exact path='/register/user' component={ User } />
<PrivateRoute exact path='/administration' component={ Admin } />
<PrivateRoute exact path='/profile' component={ Profile } />
<PrivateRoute exact path='/incidents/new' component={ NewIncident } />
</Switch>
</BrowserRouter>
</AuthContext.Provider>
)
};
Upvotes: 0
Views: 1497
Reputation: 78850
isAuthenticated
is an async function, so you'd have to await the result before you can use it. But it's more complicated than that. Your PublicRoute
function is a component, and components must synchronously return the content you want to render (at least until we get suspense). Since isAuthenticated
is async, that means you have to render twice: once while the result of isAuthenticated
is being determined, then again after. The easiest way to do that is with using state:
import { useEffect, useState } from 'react';
function PublicRoute( { component: Component, ...rest } ) {
const { authTokens } = useAuth();
const [isAuthenticated, setIsAuthenticated] = useState(null);
useEffect(() => {
isAuthenticated(authTokens).then(setIsAuthenticated);
async function isAuthenticated(token) {
if ( token === undefined ) return false;
try {
const response = await api.post( '/cT', { token } );
return response.status === 200;
} catch ( error ) {
console.log( 'Retorned false with error.' );
console.log( error );
return false;
};
}
}, [authTokens]);
console.log( 'Is authenticated?' );
console.log( isAuthenticated ); // Will be null (unknown), true, or false
if (isAuthenticated === null) {
// Render nothing for now; component will re-render after auth check
return null;
}
return (
<Route { ...rest }
render={
( props ) => ( isAuthenticated ) ?
( <Redirect to='/profile' /> ) :
( <Component { ...props } /> )
}
/>
);
}
Your component now returns content that React can handle instead of a Promise, and it handles the asynchronicity through re-renders. Your next focus should be "what should users see while they're waiting?" The approach this takes is to return null
so the route isn't exposed, but you may want to consider rendering spinners which might require this auth check function being moved somewhere else in your component tree.
Upvotes: 1
Reputation: 22817
async
is always paired with await
keyword:
const estaAutenticado = await isAuthenticated( authTokens );
^^^^^
Now you can access the value in estaAutenticado
.
Upvotes: 0