2

I want to check if user is authenticated in my React application. Using this guide.

I wrote a wrapper over my <Route /> class that check, if user is authenticated, then we render component, if not, we just redirect him to sign-in page.

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

And I use it in my router like this:

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')
)

I want to execute my async request to the server to check if user is authenticated. I've tried to add async keyword to my functions over await call, but it produces an error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.. I almost tried to use promises, but it isn't help too. When I use Promise inside my function and return <Route /> in .then() operator, React says me: IsAuthenticatedRoute(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.

So I expect to handle my async function, and then after I get response from server, give access to my user to visit this page. Is it possible only with sending synchronous request to my server or there're another ways to keep my code async and pass user to the protected page?

1
  • Because you are returning a Promise there - not a component. You should rather create HOC for secured routes, check permissions there (by showing some loader or whatever during that async check) and in case of user being not permitted to view this component do the redirect. Commented Dec 28, 2018 at 14:27

1 Answer 1

4

An async function cannot be rendered as a component, because you'd be rendering a Promise, not a pure function. Pure functions can be rendered, if they return an instance of a component. Promises must be resolved before they can be rendered.

The solution is to start the asynchronous call when the component is mounted and make the component stateful, so that it can mutate when the call is resolved. You will need to render something while waiting for a response. You can render null, but a loading spinner would be more appropriate. This way we have something to render at all times and won't run into errors trying to render a component that isn't defined yet.

Here's my quick hack at what the component could look like:

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} />} />
  )
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.