如何在React-Router包装器中处理异步请求

时间:2018-12-28 14:18:08

标签: javascript reactjs async-await react-router

我想检查用户是否在我的React应用程序中通过了身份验证。使用this指南。

我在<Route />类上写了一个包装,检查用户是否已通过身份验证,然后渲染组件,否则,我们将其重定向到登录页面。

const IsAuthenticatedRoute = function ({ component: Component, ...rest }) {
    return (
        <Route {...rest} render={async (props) => {
            return (
                await store.isAuthenticatedAsync() === true // here is the point of troubles
                    ? <Component {...props} />
                    : <Redirect to={{
                        pathname: '/sign-in',
                        state: { from: props.location }
                    }} />
            )
        }} />)
}

我像这样在路由器中使用它:

ReactDOM.render(
    <Provider store={appStore}>
        <Router>
            <div>
                <Switch>
                    <Route exact path='/' component={App} />
                    <IsAuthenticatedRoute path='/protected-route' component={Profile} />
                </Switch>
            </div>
        </Router>
    </Provider>
    ,
    document.getElementById('root')
)

我想对服务器执行异步请求,以检查用户是否已通过身份验证。我尝试通过async调用将await关键字添加到函数中,但是会产生错误:Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.。我几乎尝试过使用诺言,但这也无济于事。当我在函数中使用Promise并在<Route />运算符中返回.then()时,React对我说:IsAuthenticatedRoute(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.

因此,我希望处理我的异步功能,然后在收到服务器的响应后,向我的用户授予访问此页面的权限。是否只有通过向我的服务器发送同步请求才有可能,或者还有其他方法可以使我的代码保持异步并将用户传递到受保护的页面?

1 个答案:

答案 0 :(得分:1)

async函数无法呈现为组件,因为您将呈现Promise而非纯函数。如果纯函数返回组件的实例,则可以呈现它们。必须先解决承诺,然后才能呈现它们。

解决方案是在装入组件时启动异步调用,并使组件处于有状态,以便在调用解决后它可以发生变化。您需要在等待响应时渲染一些东西。您可以渲染null,但是加载微调器更为合适。这样,我们就可以随时渲染某些东西,并且在渲染尚未定义的组件时不会出错。

这是我快速了解该组件的外观:

class RouteRender extends React.Component {
  constructor(props) {
    super(props)
    this.state = { authorized: null }
  }

  componentDidMount() {
    // setState is called once the asynchronous call is resolved.
    store.isAuthenticatedAsync().then(
      authorized => this.setState({ authorized})
    )
  }

  render() {
    if(this.state.authorized === true) {
      const { component: Component, componentProps } = this.props
      return <Component {...componentProps} />
    } else if(this.state.authorized === false) {
      return (<Redirect to={{
               pathname: '/sign-in',
               state: { from: props.location }
             }} />)
    }
    return <LoadingSpinner />
  }
}

const IsAuthenticatedRoute = function ({ component: Component, ...rest }) {
  return (
    // render is now a function rather than a Promise.
    <Route {...rest} render={props => <RouterRender componentProps={props} component={Component} />} />
  )
}