8

我正在开发一个带有登录页面的应用程序和应用程序的其余页面(应该登录才能查看)。我正在使用 react-boilerplate. 从此example,我asyncInjectors.js将文件编辑为具有redirectToLoginredirectToDashboard方法:

//asyncInjectors.js
export function redirectToLogin(store) {
  return (nextState, replaceState) => {
    const isAuthenticated = store.getState().get('app').get('isAuthenticated');

    if (!isAuthenticated) {
      replaceState({
        pathname: '/login',
        state: {
          nextPathname: nextState.location.pathname,
        },
      });
    }
  };
}

export function redirectToDashboard(store) {
  return (nextState, replaceState) => {
    const isAuthenticated = store.getState().get('app').get('isAuthenticated');

    if (isAuthenticated) {
      replaceState('/');
    }
  }
}

然后我只是将 设置redirectToLoginonEnter页面和redirectToDashboard登录页面。

它工作正常,但是当登录时刷新页面 ( F5) 时,登录页面会短暂呈现,然后呈现实际页面。登录页面只是调度一个authenticate动作componentWillMount,然后重定向componentDidUpdate

//login.js
componentWillMount() {
  this.props.dispatch(authenticate());
}

componentDidUpdate(prevProps, prevState) {
  if (this.props.isAuthenticated) {
    const nextPathname = prevProps.location.state ? prevProps.location.state.nextPathname : '/';

    browserHistory.push(nextPathname);
  }
}

页面的容器也有相同的componentWillMount代码。不知道是不是因为传奇,但这里是代码:

//sagas.js
export function* login({ user, password }) {
    try {
        const token = yield call(app.authenticate, {
            strategy: 'local',
            user,
            password,
        });

        return token;
    } catch (error) {
        return onError(error);
    }
}

// For page refresh after logging in
export function* authenticate() {
    try {
        const token = yield call(app.authenticate);

        return token;
    } catch (error) {
        return onError(error);
    }
}

export function* logout() {
    try {
        const response = yield call(app.logout);

        return response;
    } catch (error) {
        return onError(error);
    }
}

export function* loginFlow() {
    while (true) {
        const request = yield take(LOGIN_REQUEST);
        const winner = yield race({
            auth: call(login, request.data),
            logout: take(LOGOUT_REQUEST),
        });

        if (winner.auth && winner.auth.accessToken) {
            yield put(actions.setAuthState(true));
        }
    }
}

export function* logoutFlow() {
    while (true) {
        yield take(LOGOUT_REQUEST);
        yield put(actions.setAuthState(false));
        yield call(logout);
        browserHistory.push('/login');
    }
}

export function* authenticateFlow() {
    while (true) {
        yield take(AUTHENTICATE);

        const response = yield call(authenticate);

        if (response && response.accessToken) {
            yield put(actions.setAuthState(true));
        }
    }
}

export default [
    loginFlow,
    logoutFlow,
    authenticateFlow,
];

如何摆脱闪烁的登录页面?

编辑:当我尝试gouroujo的答案时,我无法注销。

//asyncInjectors.js
import jwtDecode from 'jwt-decode';

export function redirectToLogin(store) {
    return (nextState, replaceState, callback) => {
        const token = localStorage.token;

        if (token) {
            const jwt = jwtDecode(token);

            if (jwt.exp <= (new Date().getTime() / 1000)) {
                store.dispatch(actions.setAuthState(false));

                replaceState({
                    pathname: '/login',
                    state: {
                        nextPathname: nextState.location.pathname,
                    },
                });
            }
        }

        store.dispatch(actions.setAuthState(true));
        callback();
    };
}

当我点击刷新时,login page不显示,但现在我无法注销。

4

1 回答 1

2

You have two way to avoid flashing the login page on initial render : make your authenticate function synced or wait the answer with a loading page.

1- Check if your token is present and valid (expiration date) client-side to choose if you have to redirect the user to the login or the dashboard page first. When the answer come back from your server you correct your initial guess but in the vaste majority you won't need to.

user landing ->

  • check the token client-side -> redirect to login if needed
  • check the token server-side -> wait answer -> re-redirect if needed

To check the token client-side you have to check the local storage. For example:

class App extends Component {
  requireAuth = (nextState, replace) => {
    if (!localStorage.token) {
      replace({
        pathname: '/login',
        state: { nextPathname: nextState.location.pathname }
      })
    }
  }
  render() {
    return (
      <Router history={browserHistory}>
        <Route path="/login" component={LoginPage} />
        <Route
          path="/"
          component={AppLayout}
          onEnter={this.requireAuth}
        > ... </Route>
     )
   }
 }

If you use a token with a relatively short expiration date, you will also have to check the expiration date, thus decode the token.

try {
      const jwt = JWTdecode(token);
      if (moment().isBefore(moment.unix(jwt.exp))) {
        return nextState;
      } else {
       replace({
        pathname: '/login',
        state: { nextPathname: nextState.location.pathname }
       })
      }
    } catch (e) {
      replace({
        pathname: '/login',
        state: { nextPathname: nextState.location.pathname }
      })
    }

2- Show a loading screen before you receive the answer from the server. add some Css transition effect on opacity to avoid the "flash"

于 2017-05-18T15:09:24.197 回答