【问题标题】:Await API calls before render private route在渲染私有路由之前等待 API 调用
【发布时间】:2020-12-24 08:07:16
【问题描述】:

我已保护通过身份验证启用 true 或 false 以允许用户访问身份验证页面的路由。每次页面加载,它都会调用 auth token 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 && res.data && 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>
  )
}

问题

当 auth 用户浏览认证页面(仪表板)时,重定向到登录页面。原因是在 ProtectedRoute 呈现为 isAuth 等于 false 后,验证令牌 API 返回。

【问题讨论】:

    标签: javascript reactjs react-hooks


    【解决方案1】:

    您可以使用初始状态 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 && res.data && 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>
      )
    }
    

    【讨论】:

    • 它不会重定向到非授权用户登录,因为返回 false。如果我将登录路由放在isAuth === null 中,用户将直接登录 UI 页面,然后重定向到仪表板,这不是我想要的。 Should be redirect to dashboard without showing login
    • 您的仪表板组件是什么样的?
    • 我已经更新了上面的代码。再次检查。它应该可以工作。
    【解决方案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 && res.data && 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 的经验,所以我尽可能地使示例接近原始示例。

    【讨论】:

    • ProtectedRoute 返回 null 时反应返回错误。 return (LoginRoute) ? &lt;LoginRoute {...props} /&gt; : &lt;&gt;&lt;/&gt;; 将运行。
    • @Abel 错误是什么?我怀疑您在代码中的某个位置调用ProtectedRoute 而没有loginState。在这种情况下,使用组件的默认道具。我已经更新了答案。
    【解决方案3】:

    在初始化路由之前,您可以使用另一个状态变量等待 api 执行。

    const [isAuth, setIsAuth] = useState(false)
    const [checked, setChecked] = useState(false)
      useLayoutEffect(() => {
        (async() => {
          if(accessToken){
            let res = await ValidateLoginToken(accessToken)
            if (res && res.data && 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> 
    

    我通常在单独的钩子中分离会话路由和没有会话路由,它工作正常。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-27
      • 2021-04-08
      • 2020-05-17
      • 2019-12-31
      • 1970-01-01
      相关资源
      最近更新 更多