PrivateRoute - 如何等待异步响应?

IT技术 javascript reactjs
2021-05-17 03:07:35

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

应用程序.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;

身份验证.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>
  );

}
4个回答

不是isAuthenticated在 all中调用而是PrivateRoutes你的中调用一次,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()返回一个被视为真值的 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 } }} />
      )
    }
    }
  />
);

我认为这应该有效,除非组件renderpropRoute不允许异步函数。

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

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
    }
  />
);

为了等待Promise解决您可以显示加载消息时的结果isAuthenticated()undefined

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 } }} />
}