Warten Sie auf API-Aufrufe, bevor Sie eine private Route rendern
Ich habe Routen geschützt, bei denen die Übergabe der Authentifizierung true oder false aktiviert hat, damit Benutzer auf die Authentifizierungsseite zugreifen können. Bei jedem Laden der Seite wird die Auth-Token-API aufgerufen und auf Gültigkeit überprüft. Wenn das Token ungültig ist, leiten Sie zur Anmeldeseite weiter.
ProtectedRoute.js
const ProtectedRoute = ({isEnabled, ...props}) => {
return (isEnabled) ? <Route {...props} /> : <Redirect to="/login"/>;
};
export default ProtectedRoute;
Routes.js
import {withRouter, Switch, Route } from "react-router-dom";
export default withRouter(({ location }) => {
const [isAuth, setIsAuth] = useState(false)
useLayoutEffect(() => {
(async() => {
if(accessToken){
let res = await ValidateLoginToken(accessToken)
if (res && res.data && res.data.status === 200){
setIsAuth(res.data.valid)
} else setIsAuth(false);
} else setIsAuth(false)
})()
},[isAuth])
return (
<Switch>
<ProtectedRoute path="/dashboard" component={Dashboard} isEnabled={isAuth} />
<Route path="/" component={Login} />
</Switch>
)
}
App.js.
const history = createBrowserHistory();
function App() {
return (
<Router history={history}>
<Routes/>
</Router>
)
}
export default App;
Dashboard.js
export const Dashboard = () => {
return (
<div class="ui form-user center raised padded segment">
<a href="/section1">
<div id="section1" class="ui floated right basic red button">
Directory #1
</div>
</a>
</div>
)
}
Problem
Wenn Benutzer authentifizierte Seiten (Dashboard) durchsuchen, werden sie zur Anmeldeseite weitergeleitet. Der Grund dafür ist, dass die Überprüfungstoken-API zurückgegeben wird, nachdem ProtectedRoute als isAuth gerendert wurde, was false entspricht.
Antworten
Sie können den Anfangszustand verwenden null, um die verschiedenen Zustände Ihrer Komponente zu unterscheiden.
- null -> API-Token noch nicht aufgerufen.
- true -> Token verifiziert
- false -> Token-Überprüfung fehlgeschlagen.
export default function ProtectedRoute(props) {
const [isAuth, setIsAuth] = React.useState(false)
const history = useHistory();
React.useEffect(() => {
async function validateToken() {
if(accessToken){
let res = await ValidateLoginToken(accessToken)
if (res && res.data && res.data.status === 200){
setIsAuth(true)
return;
}
}
history.push('/login')
}
validateToken();
}, [isAuth]);
if (isAuth === null) return null;
return props.children;
}
export default function App() {
return (
<Router>
<Switch>
<Route path="/" exact component={HomePage} />
<Route exact path="/login" component={Login} />
<ProtectedRoute>
<Route path="/dashboard" component={Dashboard} />
</ProtectedRoute>
</Switch>
</Router>
)
}
Anstatt isAuthein Boolescher Wert zu sein, verwenden Sie eine Zeichenfolge oder Zahl, um die Anzahl der Zustände zu erhöhen. Zum Beispiel umbenennen, isAuthin loginStatedie sein kann "pending", "authenticated"oder "unauthenticated". Verwenden Sie dann "pending"als Ausgangszustand und fügen Sie ein zusätzliches Szenario hinzu. Sie können beispielsweise zurückkehren null, um nichts zu rendern, einen sich drehenden Kreis zu rendern usw.
Hier ist ein Beispiel für das Rendern null(nichts), während das Anmeldetoken authentifiziert wird:
ProtectedRoute.js
const loginRoutes = {
pending: ( ) => null,
authenticated: (...props) => <Route {...props} />,
unauthenticated: ( ) => <Redirect to="/login"/>,
};
const ProtectedRoute = ({loginState = "pending", ...props}) => {
const LoginRoute = loginRoutes[loginState];
return <LoginRoute {...props} />;
};
export default ProtectedRoute;
Route.js
import { withRouter, Switch, Route } from "react-router-dom";
export default withRouter(({ location }) => {
const [loginState, setLoginState] = useState("pending")
useLayoutEffect(() => {
(async() => {
if(accessToken){
let res = await ValidateLoginToken(accessToken)
if (res && res.data && res.data.status === 200) {
setLoginState(res.data.valid ? "authenticate" : "unauthenticated")
} else setLoginState("unauthenticated");
} else setLoginState("unauthenticated")
})()
}, [loginState])
return (
<Switch>
<ProtectedRoute path="/dashboard" component={Dashboard} loginState={loginState} />
<Route path="/" component={Login} />
</Switch>
)
}
Als Haftungsausschluss habe ich keine Erfahrung mit React Router, daher habe ich das Beispiel so nah wie möglich am Original gehalten.
Sie können eine andere Statusvariable verwenden, um auf die API-Ausführung zu warten, bevor Sie Routen initialisieren.
const [isAuth, setIsAuth] = useState(false)
const [checked, setChecked] = useState(false)
useLayoutEffect(() => {
(async() => {
if(accessToken){
let res = await ValidateLoginToken(accessToken)
if (res && res.data && res.data.status === 200){
setIsAuth(res.data.valid)
} else setIsAuth(false);
} else setIsAuth(false)
setChecked(true)
})()
},[isAuth])
Dann können Sie in Routen Folgendes tun:
<Switch>
{
!checked?(<React.Fragment/>):!isAuth?(<Route path="/" component={Login} />):(<ProtectedRoute path="/dashboard" component={Dashboard} loginState={loginState} />)
}
</Switch>
Normalerweise trenne ich Sitzungsrouten und keine Sitzungsrouten in separaten Hooks und es funktioniert gut.