带有Redux的基于专用和公用路由的身份验证无法正常工作

时间:2020-03-28 09:05:07

标签: reactjs redux react-redux react-router

我正在尝试在我正在从事的项目上实施私有,受保护和公共路线。每次刷新页面时,我都会在app.js的componentDidMount内部调度我的setUserAsync()操作创建者,该调用者会调用后端,以基于登录或注册时分配的令牌询问用户信息。 SetUserAsync设置用户,并在收到用户后将名为isAuthenticated的状态切换为true。然后,我使用isAuthenticated状态来确定要在我的私有和公共路由中呈现或重定向到的组件。

但是我面临的问题是,每当我尝试访问任何私有或受保护的路由时,都会基于isAuthenticated的初始状态false来立即触发三元运算符。因此,在像/ dashboard这样的专用路线上,它将带​​我进入/ signin,因为isAuthenticated最初为false,直到由app.js的componentDidMount中的setUserAsync()设置为true为止。然后从/ signin重定向到/ dashboard,因为/ signin是受保护的路由,并在isAuthenticated最终变为true时将我带回到/ dashboard。

如何做到这一点,以便刷新时的路由在呈现任何内容之前等待API调用完成。我已经看到许多人正在实施相同的流程,这对他们来说很好,但是我不知道我在做什么错。

//App.js

class App extends React.Component {

componentDidMount() {
const { setUserAsync, token, tokenExpiry } = this.props;
token ? setUserAsync(token) : null
}
 render() {
  return (
  <Switch>
    <PublicRoute exact path="/" component={Home} />
    <PublicRoute exact path="/about-us" component={AboutUs} />
    <PublicRoute exact path="/contact-us" component={ContactUs} />
    <PublicRoute exact path="/privacy-policy" component={PrivacyPolicy} />
    <PublicRoute exact path="/terms-of-use" component={TermsOfUse} />

    <ProtectedRoute exact path="/signin" component={SignIn} />
    <ProtectedRoute exact path="/signup" component={SignUp} />

    <PrivateRoute exact path="/dashboard" component={Dashboard} />
    <PrivateRoute exact path="/all-leads" component={AllLeads} />
   </Switch>
 )};

const mapStateToProps = createStructuredSelector({
 token: selectUserToken,
 tokenexpiry: selectTokenExpiry
});

const mapDispatchToProps = dispatch => ({
 setUserAsync: (token) => dispatch(setUserAsync(token))
});

export default connect(mapStateToProps, mapDispatchToProps)(App);




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

const mapStateToProps = createStructuredSelector({
  isAuthenticated: selectIsUserAuthenticated,
});

export default connect(mapStateToProps)(PrivateRoute);


//ProtectedRoute - signin & signup
const ProtectedRoute = ({component: Component, isAuthenticated, isFetching, ...rest}) => {
    return !isFetching ? (
     <Route
      {...rest}
    render={(props) => 
      isAuthenticated
      ? <Redirect to={{pathname: '/dashboard', state: {from: props.location}}} />
      : <Component {...props} /> }
   />
  )
  : null
}

const mapStateToProps = createStructuredSelector({
  isAuthenticated: selectIsUserAuthenticated,
  isFetching: selectIsFetching
});

export default connect(mapStateToProps)(ProtectedRoute);


//User reducer

const INITIAL_STATE = {
  isAuthenticated: false,
  currentUser: null
};

const UserReducer = (state = INITIAL_STATE, action) => {
  const { type, payload } = action;
  switch (type) {
case UserActionTypes.FETCH_USER_SUCCESS: {
  return {
    ...state,
    isAuthenticated: true,
    currentUser: payload
  };
}
case UserActionTypes.FETCH_USER_ERROR: {
  return {
    ...state,
    isAuthenticated: false,
    currentUser: null
  };
}
default:
  return state;
  }
};

export default UserReducer;

1 个答案:

答案 0 :(得分:0)

您使用哪种身份验证?

如果您使用JWT并将令牌存储在localStorage中,则可以执行以下操作:

const isAuthorized = isLoggedIn || localStorage.get('token');
/* isLoggedIn - user has just logged in with Login Page,
localStorage.get('token') - user refreshed page and he has a token,
you don't want to redirect him until token fails */

因此,您认为具有令牌的用户已被授权,但是他看不到任何后端数据,因为他必须提取该数据。

如果他的令牌无效,后端将返回401响应,您将清除用户令牌并将其重定向到登录。

您还可以使用两个Switch块。

const renderPrivateRoute = (item, index) => (
  <PrivateRoute key={index} {...item} />
);
const renderPublicRoute = (item, index) => (
  <PublicRoute key={index} {...item} />
);
/* ... */
{
  isAuthorized ?
(<Switch>
    {privateRoutes.map(renderPrivateRoute)} 
    <Route
        path="*"
       render={() => <Redirect to={{ pathname: DEFAULT_PRIVATE_ROUTE }} />}
    />
</Switch>) :
(<Switch>
    {publicRoutes.map(renderPublicRoute)}
    <Route
       path="*"
        render={() => <Redirect to={{ pathname: DEFAULT_PUBLIC_ROUTE }} />}
    />
</Switch>)
}

使用React Suspense的第二种方法,但仍处于试验阶段。您也可以尝试自己做类似的事情。 https://reactjs.org/docs/concurrent-mode-suspense.html

第三种方法是持久存储。 看看redux-persist npm软件包。