Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adds notes about HOC to handle client routing #3168

Closed
wants to merge 7 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions docs/docs/creating-and-modifying-pages.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,112 @@ exports.onCreatePage = async ({ page, boundActionCreators }) => {
};
```

### Client Route Params

Gatsby can create client-only routes which take paramaters.

We'll walk quickly through how to setup a route on your site like `/widgets/view/ID`.

**Build config:**

First configure `gatsby-node.js`:

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

return new Promise((resolve, reject) => {
// Add client route for all paths matching `/view/*`
if (/view/.test(page.path)) {
// Gatsby paths have a trailing `/`
page.matchPath = `${page.path}:id`;
}

createPage(page);
resolve();
});
};
```

**Server:**

Links to these pages will work well client-side but fail if a user tries to visit a page directly (i.e. load the page from the server). This is due to your backend server not knowing how to handle the route.

Some server configuration is required to make these types of pages work in production. This configuration will vary depending on your server environment but here's a simple example configuration for NGINX:

```
location ~ "([a-z]*)\/view\/(\d*)$" {
try_files $uri /$1/view/index.html;
}
```

This directive will provide the `widgets/view/index.html` file for any request like (widgets)/view/1. It will also support other pages by matching the first level of the URL, example `http://sitename/sprockets/view/1`.

**Client:**

Extracting path params from the route on the client requires that you use the `react-router` `<Route>` in your component.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised to hear this — the param information should be available without any more work on props.location — is this not so?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya I was under the impression from convos in discord that you still needed to create client routes for the page. I could not access the route Params without it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you verify what the props.location looks like w/o the custom client routes? If it's not working, we should fix that in Gatsby's auto-created route components as it's a lot of overhead to make people add their own route components.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will check tomorrow, fairly certain it was empty without the route.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Params are not passed to components.

I searched repository for matchPath and it seems gatsby only tests if path is matched and discard return value of react-router matchPath (which would have extracted params):

if (page.matchPath) {
// Try both the path and matchPath
if (
matchPath(trimmedPathname, { path: page.path }) ||
matchPath(trimmedPathname, {
path: page.matchPath,
})
) {
foundPage = page
pageCache[trimmedPathname] = page
return true
}
} else {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the research @pieh — so yeah, we need to fix the problem there — pass into the component the props for the matched path. @gregorskii want to tackle this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep I will look into it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gregorskii btw, do you think you'll still have a chance to look into this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@KyleAMathews I have not had a chance to look into it. I got pulled into another project with a short timeline at work.

I can try to look at it after, but if its a quick fix, does someone else have time?

I was able to solve this on my project with the proposed fix in this original PR, however as discussed my work around may not be needed.


This can be made simpler by using a HOC:

```javascript
import React from 'react';
import PropTypes from 'prop-types';
import { Route } from 'react-router-dom';

// Pass in route config, and the Content component you want rendered
export default (config, Content) => {
const GatsbyClientRoute = (props) => {
return (
<Route
{...props}
{...config}
component={Content}
/>
)
};

return GatsbyClientRoute;
};
```

Use the HOC on the page component you want to access the path params:

```javascript
export default GatsbyClientRoute({path: '/widgets/view/:id'}, WidgetPage);
```

Full example page:

```javascript
import React from 'react';

import GatsbyClientRoute from '<PATH_TO_SRC>/components/hocs/gatsby-client-route';

class WidgetPageContent extends React.Component {
constructor(props) {
super(props);
}

render() {
const { match } = this.props;
console.log(match.params.id);
return (
<div>
Widget: {match.params.id}
</div>
);
}
};

export default GatsbyClientRoute(
// NOTE this must match path.matchPath
{path: '/widgets/view/:id'},
WidgetPageContent
);
```

Using the URL `http://localhost:8000/wigets/view/10` will console log `10` and the markup will say `Widget: 10`.

### Choosing the page layout

By default, all pages will use the layout found at `/layouts/index.js`.
Expand Down