Matt
Matt

Reputation: 821

Restrict routes programmatically in Gatsby

In Gatsby, how would I restrict routes programmatically? Using react-router, I see it's possible to do a <Redirect> with <Route>, but how would this be implemented in Gatsby? To do something like this...

<Route exact path="/" render={() => (
  loggedIn ? (
    <Redirect to="/dashboard"/>
  ) : (
    <PublicHomePage/>
  )
)}/>

Where would I put this file in Gatsby? Would I put it in src/pages or elsewhere?


Edited, asking for additional clarification...

I'm able to get this work per the advice from @Nenu and the Gatsby docs. The docs gave a non-asynchronous example, so I had to tweak it for interacting with a remote server like this...

async handleSubmit(event) {
  event.preventDefault()
  await handleLogin(this.state)
    .then(response => _this.setState({isLoggedIn: isLoggedIn()}))
    .catch(err => { console.log(err) });
}

Also, I am able to use the <PrivateRoute /> with this.

Unfortunately though, when I render using...

render() {
  if (isLoggedIn()) {
    return <Redirect to={{ pathname: `/app/profile` }} />
  }

  return (
    <View title="Log In">
      <Form
        handleUpdate={e => this.handleUpdate(e)}
        handleSubmit={e => this.handleSubmit(e)}
      />
    </View>
  )
}

...while I do indeed <Redirect to={{ pathname:/app/profile}} />, I notice that a split-second before I redirect, the form fields are emptied and only after that do I get redirected to /app/profile (from /app/login). Also, if I type in an incorrect password, my whole form is re-rendered (re-rendering <View /> again). This would be a bad user-experience because they they'd have to re-enter all of their info from scratch, plus I'd be unable to add styling for invalid inputs, etc. I'm wondering if there is a better way to do this with Gatsby.

Or, would I have to build form functionality more from scratch (i.e., using Redux, Router, etc more directly) rather than depending upon Gatsby's higher level of abstraction?

Upvotes: 3

Views: 6467

Answers (1)

Nenu
Nenu

Reputation: 2657

Gatsby uses react-router under the hood, therefore you can define your client-only routes with it.

There is, as always with gatsby, a very nice example in the github repo:

https://github.com/gatsbyjs/gatsby/tree/master/examples/simple-auth

And the doc about it:

https://www.gatsbyjs.org/docs/building-apps-with-gatsby/#client-only-routes--user-authentication

To sum up, this is what is done:

1) Create a PrivateRoute component in /src/components

const PrivateRoute = ({ component: Component, ...rest }) => (
  <Route
    {...rest}
    render={props =>
      !isLoggedIn() ? (
        // If we’re not logged in, redirect to the login page.
        <Redirect to={{ pathname: `/app/login` }} />
      ) : (
        <Component {...props} />
      )
    }
  />
);

2) Define routes in your wanted prefix "client-only" path

Let suppose you want to restrict access of the /app/:path section of your website, then in /src/pages/app.js:

const App = () => (
  <div>
    <PrivateRoute path="/app/profile" component={Home} />
    <PrivateRoute path="/app/details" component={Details} />
    <Route path="/app/login" component={Login} />
  </div>
);

These routes will exist on the client only and will not correspond to index.html files in an app’s built assets. If you wish people to visit client routes directly, you’ll need to setup your server to handle these correctly. (source)

3) White-list the client-routes in gatsby-node.js

exports.onCreatePage = async ({ page, boundActionCreators }) => {
  const { createPage } = boundActionCreators

  // page.matchPath is a special key that's used for matching pages
  // only on the client.
  if (page.path.match(/^\/app/)) {
    page.matchPath = `/app/:path`

    // Update the page.
    createPage(page)
  }
}

Upvotes: 7

Related Questions