#javascript #reactjs #react-hooks
#javascript #reactjs #реагирующие крючки
Вопрос:
У меня есть защищенные маршруты, для которых при передаче аутентификации включено значение true или false, чтобы разрешить пользователям доступ к странице аутентификации. При каждой загрузке страницы он вызывает API токена аутентификации и проверяет правильность. Если токен недействителен, перенаправьте на страницу входа.
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 amp;amp; res.data amp;amp; 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>
)
}
Проблема
Когда авторизованные пользователи просматривают страницу аутентификации (панель мониторинга), перенаправляется на страницу входа. Причина в том, что verify token API возвращается после того, как ProtectedRoute отображается как isAuth, равный false.
Ответ №1:
Вы можете использовать другую переменную состояния для ожидания выполнения api перед инициализацией маршрутов.
const [isAuth, setIsAuth] = useState(false)
const [checked, setChecked] = useState(false)
useLayoutEffect(() => {
(async() => {
if(accessToken){
let res = await ValidateLoginToken(accessToken)
if (res amp;amp; res.data amp;amp; res.data.status === 200){
setIsAuth(res.data.valid)
} else setIsAuth(false);
} else setIsAuth(false)
setChecked(true)
})()
},[isAuth])
Затем в Routes вы можете сделать что-то вроде:
<Switch>
{
!checked?(<React.Fragment/>):!isAuth?(<Route path="/" component={Login} />):(<ProtectedRoute path="/dashboard" component={Dashboard} loginState={loginState} />)
}
</Switch>
Обычно я разделяю сеансовые маршруты и маршруты без сеансов в отдельных перехватах, и это работает нормально.
Ответ №2:
Вместо isAuth
логического значения вы используете строку или число для увеличения числа состояний. Например, переименовать isAuth
в loginState
, который может быть "pending"
, "authenticated"
или "unauthenticated"
. Затем используйте "pending"
в качестве начального состояния и добавьте дополнительный сценарий. Вы могли бы, например, вернуться null
, чтобы ничего не отображать, отображать вращающийся круг и т.д.
Вот пример рендеринга null
(ничего) во время аутентификации токена входа:
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 amp;amp; res.data amp;amp; 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>
)
}
В качестве оговорки, у меня нет опыта работы с React Router, поэтому я сохранил пример как можно ближе к оригиналу.
Комментарии:
1. Реакция возвращает ошибку, когда ProtectedRoute возвращает null.
return (LoginRoute) ? <LoginRoute {...props} /> : <></>;
будет выполняться.2. @Abel В чем ошибка? Я подозреваю, что вы вызываете
ProtectedRoute
безloginState
где-либо в своем коде. В этом случае используйте prop по умолчанию для компонента. Я обновил ответ.
Ответ №3:
Вы могли бы использовать начальное состояние как null
для различения различных состояний вашего компонента.
- null -> токен API еще не вызван.
- true -> токен проверен
- ошибка false -> ошибка проверки токена.
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 amp;amp; res.data amp;amp; 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>
)
}
Комментарии:
1. Он не будет перенаправлять на вход для неавторизованного пользователя, потому что возвращает false. Если я разместил маршрут входа в
isAuth === null
, пользователи будут перенаправлять страницу пользовательского интерфейса входа, а затем перенаправлять на панель мониторинга, чего я не хотел.Should be redirect to dashboard without showing login
2. Как выглядит ваш компонент панели мониторинга?
3. Я обновил приведенный выше код. Проверьте еще раз. Это должно работать.