【问题标题】:PrivateRoute - how to wait a response from async?PrivateRoute - 如何等待来自异步的响应?
【发布时间】:2020-05-17 16:01:39
【问题描述】:

我有一个 PrivateRoute 来验证令牌是否有效并且它是一个异步函数。问题是:我对渲染视图的验证不起作用,它总是在渲染:

App.js

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

const Routes = () => (
  <BrowserRouter>
    <Fragment>
      <Switch>
        <Route exact path="/" component={SignIn} />
        <PrivateRoute path="/app" component={App} />
      </Switch>
      <ModalContainer />
    </Fragment>
  </BrowserRouter>
);

export default Routes;

auth.js

import axios from 'axios'

export const isAuthenticated = async () => {
  const isValidRequest = false;

  const resp = await axios.get('http://127.0.0.1:8080...')
  data = resp.data;
  console.log(data);

  ....// more code

  return isValidRequest;  
}

如何确保 PrivateRoute 将等待函数 isAuthenticated()

更新1:

const Routes = () => {
  const [state, setState] = useState({isLoading: true, authenticated: false});
  useEffect(() => {
    async function checkAuth() {
        const isAuth = await isAuthenticated();
        setState({isLoading: false, authenticated: isAuth});
    }
  }, []);

  if(state.isLoading) {
     return <div>Loading....</div>
  }
  return (
    <BrowserRouter>
      <Fragment>
        <Switch>
          <Route exact path="/" component={SignIn} />
          <PrivateRoute path="/app" isAuthenticated={state.authenticated} component={App} />
        </Switch>
        <ModalContainer />
      </Fragment>
    </BrowserRouter>
  );

}

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    不是在所有PrivateRoutes 中调用isAuthenticated,而是在Routes component 中调用它一次,以便只执行一次检查,然后将值作为prop 传递。另请注意,在获取数据时保持加载状态

    请注意,您的 isAuthenticated 函数是一个异步函数,因此您必须等到 promise 得到解决。你可以使用 async-await 或者使用传统的 promise 方法

    const PrivateRoute = ({ component: Component, isAuthenticated, ...rest }) => (
      <Route
        {...rest}
        render={props =>
          isAuthenticated ? (
            <Component {...props} />
          ) : (
            <Redirect to={{ pathname: "/", state: { from: props.location } }} />
          )
        }
      />
    );
    
    const Routes = () => {
        const [state, setState] = useState({isLoading: true, authenticated: false});
        useEffect(() => {
          async function checkAuth() {
             const isAuth = await isAuthenticated();
             setState({isLoading: false, authenticated: isAuth});
         }
          checkAuth();
       }, []);
        if(state.isLoading) {
           return <Loader/>
        }
        return (
          <BrowserRouter>
            <Fragment>
              <Switch>
                <Route exact path="/" component={SignIn} />
                <PrivateRoute path="/app" isAuthenticated={state.authenticated} component={App} />
              </Switch>
              <ModalContainer />
            </Fragment>
          </BrowserRouter>
        );
    
    }
    
    export default Routes;
    

    更新:由于你没有使用react v16.8.0以上版本,所以可以通过类组件实现上述逻辑

    class Routes extends React.Component {
        state = {isLoading: true, authenticated: false};
    
        async componentDidMount() {
             const isAuth = await isAuthenticated();
             this.setState({isLoading: false, authenticated: isAuth});
        }
    
        render() {
            if(state.isLoading) {
               return <Loader/>
            }
            return (
              <BrowserRouter>
                <Fragment>
                  <Switch>
                    <Route exact path="/" component={SignIn} />
                    <PrivateRoute path="/app" isAuthenticated={state.authenticated} component={App} />
                  </Switch>
                  <ModalContainer />
                </Fragment>
              </BrowserRouter>
            );
          }
    
    }
    
    export default Routes;
    

    【讨论】:

    • 我过去做过类似的事情,它确实可以完成这项工作。
    • 什么是isAuthenticated={}?它给了我一个错误:Syntax error: JSX attributes must only be assigned a non-empty expression (42:52)
    • 同样找不到useState、useEffect和Loader:未定义
    • 好吧,Loader 是一个组件,您可以编写一个显示加载微调器的组件,只需将其替换为&lt;div&gt;Loading....&lt;/div&gt;。现在需要从reactimport React, { useState, useEffect} from 'react'导入useEffect和useState@
    • 现在我在const Routes = () =&gt; { 上遇到错误:TypeError: Object(...) is not a function
    【解决方案2】:

    这可能是因为isAuthenticated() 返回了一个被视为真值的 Promise,因此三元条件将呈现组件。

    试试这个:

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

    我认为这应该可以工作,除非 Route 组件的 render 属性不允许异步函数。

    【讨论】:

      【解决方案3】:

      我建议您存储您的进度并根据它返回:

      const PrivateRoute = ({ component: Component, ...rest }) => (
        const [finished, setFinished] = useState(false);
        const [isAuthenticated, setIsAuthenticated] = useState(false);
      
        useEffect(() => {
          isAuthenticated().then(result => {
            setFinished(true);
            setIsAuthenticated(result);
          });
        }, []);
      
        <Route
          {...rest}
          render={props =>
            finished && isAuthenticated ? (
              <Component {...props} />
            ) : finished && !isAuthenticated ? (
              <Redirect to={{ pathname: "/", state: { from: props.location } }} />
            ) : null
          }
        />
      );
      

      【讨论】:

        【解决方案4】:

        isAuthenticated() 的结果为undefined 时,您可以显示加载消息,以等待promise 被解决:

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

        【讨论】:

          猜你喜欢
          • 2016-02-14
          • 2011-09-03
          • 2013-08-29
          • 2019-06-27
          • 2020-12-03
          • 2020-12-09
          • 1970-01-01
          • 1970-01-01
          • 2017-10-18
          相关资源
          最近更新 更多