Моя проблема проста, по крайней мере, кажется. У меня есть состояние в моем хранилище redux, которое хранит состояние того, вошел ли пользователь в систему или нет. Все работает нормально, но когда пользователь обновляет страницу, на мгновение, пока аутентифицированное состояние асинхронно получает данные, выполняется рендеринг и состояние не определено.
Поскольку состояние не определено, выполняется перенаправление на / login, поэтому обновление выгоняет меня из приложения и возвращает к входу в систему, который затем проверяет, вошел ли я уже в систему, и перенаправляет меня на домашнюю страницу.
Любые идеи о том, как решить эту проблему:
{
!this.props.authenticated && (
<Switch>
<Route path = "/login" component = {LoginForm} />
<Route path = "/register" component = {RegisterForm} />
<Route path = "" render = {props => {
return <Redirect to = "/login" />
}}
/>
</Switch>
)
}
Итак, когда this.props.authenticated имеет значение false в течение этого короткого периода времени, он попадает в перенаправление входа. Но через несколько мсек this.props.authenticated истинно, и, поскольку пользователь уже вошел в систему, он перенаправляется на домашний маршрут.
Любые идеи?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


В идеале вы не должны сразу отображать свой маршрут, а подождите, пока ваш запрос аутентификации не будет разрешен и у вас будет четкое состояние.
Что-то вроде этого:
class App extends React.Component {
constructor( props ) {
super( props );
this.state = {
// You could just check if authenticated is null,
// but I think having an extra state makes is more clear
isLoading: true,
authenticated: null,
};
this.checkAuthentication();
}
checkAuthentication() {
// Some async stuff checking against server
// I’ll simulate an async call with this setTimeout
setTimeout(
() => this.setState( {
authenticated: Boolean( Math.round( Math.random() ) ),
isLoading: false,
} ),
1000
);
}
render() {
// Render a loading screen when we don't have a clear state yet
if ( this.state.isLoading ) {
return <div>loading</div>;
}
// Otherwise it is safe to render our routes
return (
<div>
routes,<br />
random authenticated:
<strong>
{ this.state.authenticated.toString() }
</strong>
</div>
);
}
}
ReactDOM.render( (
<App />
), document.querySelector( 'main' ) );<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<main></main>Хорошо, ценю твой ответ. Подал мне идею попробовать что-то другое с async / await вместо этого, что я добавлю в качестве ответа. Спасибо еще раз!
Это всего лишь пример, чтобы вы увидели, как реакция не будет отображать маршруты. Конечно, вы не должны использовать setTimeout и вместо этого ждать, пока обещание не разрешится (как в вашем ответе)
Хорошо, lumio помог мне встать на правильный путь с помощью setTimeout, поэтому вместо этого я решил это с помощью async / await:
class App extends Component {
state = {
error: "",
isLoading: true,
}
async componentDidMount() {
let token = localStorage.getItem('jwtToken');
if (token) {
setAuthToken(token);
await this.props.isAuthenticatedAction(true);
} else {
await this.props.isAuthenticatedAction(false);
}
this.setState({
isLoading: false,
});
}
handleLogout = (evt) => {
evt.preventDefault();
localStorage.removeItem('jwtToken');
window.location.reload();
}
render() {
if (this.state.isLoading) {
return <div></div>;
} else {
// return my regular content
}
Вы можете использовать response-router-dom для рабочего процесса аутентификации.
import React from "react";
import {
BrowserRouter as Router,
Route,
Link,
Redirect,
withRouter
} from "react-router-dom";
////////////////////////////////////////////////////////////
// 1. Click the public page
// 2. Click the protected page
// 3. Log in
// 4. Click the back button, note the URL each time
const AuthExample = () => (
<Router>
<div>
<AuthButton />
<ul>
<li>
<Link to = "/public">Public Page</Link>
</li>
<li>
<Link to = "/protected">Protected Page</Link>
</li>
</ul>
<Route path = "/public" component = {Public} />
<Route path = "/login" component = {Login} />
<PrivateRoute path = "/protected" component = {Protected} />
</div>
</Router>
);
const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true;
setTimeout(cb, 100); // fake async
},
signout(cb) {
this.isAuthenticated = false;
setTimeout(cb, 100);
}
};
const AuthButton = withRouter(
({ history }) =>
fakeAuth.isAuthenticated ? (
<p>
Welcome!{" "}
<button
onClick = {() => {
fakeAuth.signout(() => history.push("/"));
}}
>
Sign out
</button>
</p>
) : (
<p>You are not logged in.</p>
)
);
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render = {props =>
fakeAuth.isAuthenticated ? (
<Component {...props} />
) : (
<Redirect
to = {{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
/>
);
const Public = () => <h3>Public</h3>;
const Protected = () => <h3>Protected</h3>;
class Login extends React.Component {
state = {
redirectToReferrer: false
};
login = () => {
fakeAuth.authenticate(() => {
this.setState({ redirectToReferrer: true });
});
};
render() {
const { from } = this.props.location.state || { from: { pathname: "/" } };
const { redirectToReferrer } = this.state;
if (redirectToReferrer) {
return <Redirect to = {from} />;
}
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<button onClick = {this.login}>Log in</button>
</div>
);
}
}
export default AuthExample;
см. ссылку https://reacttraining.com/react-router/web/example/auth-workflow
Прежде всего, когда пользователь пытается войти в систему, вы получаете токен в ответ, когда пользователь аутентифицирован. теперь вам нужно сохранить токен в localStorage, используя
if (user.token){
localStorage.setItem('user', JSON.stringify(user));
}
указано, что когда у вас есть токен в localstorage, вы входите в систему, в противном случае вы выходите из системы.
Теперь попробуйте установить состояние для перенаправления на домашнюю страницу, если вы хотите перейти на домашнюю страницу после входа в систему.
this.setState({redirectToReferrer: true});
теперь верните перенаправление на страницу желаний
if (this.state.redirectToReferrer){
return (<Redirect to = {'/home'}/>)
}
login.js
import React from 'react';
import axios from 'axios';
import {Redirect} from 'react-router-dom';
export default class Login extends React.Component{
constructor(props){
super(props);
this.state = {
email : '' ,
password : '',
redirectToReferrer : false
};
this.handleChangeEvent = this.handleChangeEvent.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChangeEvent(event){
this.setState({
[event.target.name] : event.target.value
});
}
handleSubmit(event){
event.preventDefault();
const user = {
email : this.state.email,
password : this.state.password
};
if (this.state.email && this.state.password)
{
axios.post(`{Api}/login`,user)
.then((response) =>
{
let userresponse = response;
console.info(userresponse.data);
if (userresponse.token){
sessionStorage.setItem('data',JSON.stringify(userresponse));
this.setState({redirectToReferrer: true});
}
},this)
.catch((error) => alert(error))
}
}
render(){
if (this.state.redirectToReferrer){
return (<Redirect to = {'/user'}/>)
}
if (sessionStorage.getItem('data')){
return (<Redirect to = {'/user'}/>)
}
return(
<div>
<form ref = "formdemo" onSubmit = {this.handleSubmit}>
<label>
Username:
<input type = "email" name = "email" onChange = {this.handleChangeEvent} placeholder = "Enter Your EmailID" required/></label><br/>
<label>
Password :
<input type = "password" name = "password" onChange = {this.handleChangeEvent} placeholder = "Enter Your Password" required/></label><br/>
<input type = "submit"/>
</form>
</div>
)
}
}
Хорошо, я думал об этом, но разве это не плохая практика использовать setTimeout таким образом? Что, если по какой-то причине ответ вернется через 1000 мс? Тогда у меня такая же проблема.