私有路由在渲染组件之前获取身份验证

时间:2021-01-02 12:15:17

标签: reactjs redux

我在使用 PrivateRoute 进行身份验证时遇到问题。

由于我使用 redux 来捕获用户的身份验证,因此在呈现组件之前我需要等待 state 从商店返回。 如果没有,页面将自动重定向到 login route

我的问题是当前路由会在等待认证结果时重定向到登录页面。验证通过后返回页面。

所以我想先等待 authentication result。如果通过身份验证,将加载页面组件,否则将重定向到登录页面。

这是我的带有身份验证的私有路由。

import React from "react";
import { Route, Redirect} from "react-router-dom";
import { connect } from "react-redux";

const PrivateRoute = ({ component: Component, auth, role, ...rest }) => {    
  return (
    <Route
      {...rest}
      render={props => auth.isAuthenticated ? (
          <Component {...props} />
        ) : (          
          <Redirect  to={{
            pathname: "/",
            state: { from: props.location }
          }} />
        )
      }
    />
  )
}

const stateStoreToProp = state => ({
  auth: state.loginReducer,
});
export default connect(stateStoreToProp)(PrivateRoute);

这是我的自动重定向登录组件。

  let history = useHistory();
  let location = useLocation();

  let { from } = location.state || { from: { pathname: "/choose" } };
  const isAuthenticated = prop.loginReducer.isAuthenticated;
  const user = prop.loginReducer.user;
  if (isAuthenticated && typeof user !== "undefined") {
    history.replace(from);
  }

这里是 App.js

import { loadUser } from './actions/AuthAction';
function App() {
    useEffect(() => {
      store.dispatch(loadUser()); 
    }, []);
    return (
      <Provider store={store}>
        <Router>
          <Fragment>
            <div className="blockcontain">
              <div className="fss">
                <div className="rightSide">
                  <Switch>
                    <Route path='/' exact component={Login} />
                    <PrivateRoute exact path='/choose' component={Choose} />
                  </Switch>
                </div>
              </div>
            </div>
          </Fragment>
        </Router>
      </Provider>
    );  
}

和 LoginReducer

import {
  LOGIN_SUCCESS, LOGIN_FAIL, USER_LOADED, AUTH_ERROR
} from '../actions/typeName';
import Cookies from 'js-cookie';
const initialState = {
  token: Cookies.get('token'),
  isAuthenticated: null,
  loading: true, 
  user: null
}
const loginReducer = (state = initialState, action) => {
  const {type, payload} = action; 

  switch(type) {
      case USER_LOADED:
        return {
          ...state,
          isAuthenticated: true,
          loading: false,
          user: payload
        };
      case LOGIN_SUCCESS:
        Cookies.set('token', payload.access_token, { expires: payload.expires_at });
        return {
          ...state,
          isAuthenticated: true, 
          loading: false
        }
      case AUTH_ERROR:
      case LOGIN_FAIL:
          return {
            ...state,
            token:null,
            isAuthenticated: false,
            loading: true
          }
      default:
          return state;
  }
}

export default loginReducer;

我去choose page,它会去登录页面,因为store for authentication没有准备好。

谢谢。

2 个答案:

答案 0 :(得分:1)

你可以在你的状态中添加一个加载属性,并且只在获取完成时显示路由:

const PrivateRoute = ({ component: Component, auth, isLoading, role, ...rest }) => {
  if(isLoading) return <div>Loading...</div>;
  
  return (
    <Route
      {...rest}
      render={(props) =>
        auth.isAuthenticated ? (
          <Component {...props} />
        ) : (
          <Redirect
            to={{
              pathname: '/',
              state: { from: props.location },
            }}
          />
        )
      }
    />
  )
}

const stateStoreToProp = (state) => ({
  isLoading: state.loading,
  auth: state.loginReducer,
})

export default connect(stateStoreToProp)(PrivateRoute)

并在你的减速器中添加加载突变,例如:

case REQUEST_LOGIN:
      return [...state, isLoading: true];
case LOGIN_SUCCESSFUL:
      return [...state, auth: isAuthenticated, isLoading: false];

答案 1 :(得分:1)

您可以做的是在您的身份验证状态中添加一个“加载”密钥,因此在进行身份验证时,状态将为“真”,否则为“假”。如果是真的,你会显示一个加载器。

import React from "react";
import { Route, Redirect} from "react-router-dom";
import { connect } from "react-redux";

const PrivateRoute = ({ component: Component, auth, role, ...rest }) => {
    
  return (
    <>
      {
        auth.loading && (
          <Loader/>
        )
      }

      {
        !auth.loading && (
          (
            <Route
            {...rest}
            render={props => auth.isAuthenticated ? (
                <Component {...props} />
              ) : (          
                <Redirect  to={{
                  pathname: "/",
                  state: { from: props.location }
                }} />
              )
            }
          />          
          )          
        )
      }
    </>
  )
}


const stateStoreToProp = state => ({
  auth: state.loginReducer,
});
export default connect(stateStoreToProp)(PrivateRoute);