gatsby-plugin-intl icon indicating copy to clipboard operation
gatsby-plugin-intl copied to clipboard

Added support for dynamic path params in client-only routes

Open Hless opened this issue 5 years ago • 14 comments

This is a proposed fix to support dynamic path parameters in routes.

Suppose you have a path called: /user/:userId . Version 0.3.x would not correctly pick up /user/14 for example, but would instead show a 404 page.

With this PR I have implemented redirection (from /user/14 to /en/user/14 for example), while replacing the named path parameters.

This PR does not (yet) include support for wildcard path matching using matchPath, but seeks to forgo that in favour of named path params. Currently this is because the changes in the PR make use of matchPath in order to match against paths without lang code prefix.

See issue #68, #93, #101

Hless avatar May 22 '20 12:05 Hless

This would be great to see! Any movement here?

justintemps avatar Jun 29 '20 07:06 justintemps

Hi @Hless, how did you implement your client only routes ? When I define them in gatsby-node.js, your PR does not seem to fix the issue.

// gatsby-node.js
exports.createPage = async ({ actions }) => {
  actions.createPage({
    path: "/route/:param",
    matchPath: "/route/:param",
    component: path.resolve("./src/template/route.tsx")
  })
}

When I try to access /en/route/12/ for example, I get a 404.

fabienheureux avatar Jul 01 '20 11:07 fabienheureux

Sorry for the late reply! Just came back from vacation. I have defined my routes as follows:

createPage({
    path: "blog/:slug",
    component: getTemplate("blog/blog"),
})

Big difference being that I omitted matchPath altogether. As stated in the description, there are several issues with matchPath and this plugin that should be addressed first.

Edit: Excuse me, just read my own description more carefully. This PR uses matchPatch in order to match against languages, so named parameters for localized routes and matchPath functionality are mutually exclusive!

Hless avatar Jul 09 '20 07:07 Hless

Sorry for the late reply! Just came back from vacation. I have defined my routes as follows:

createPage({
    path: "blog/:slug",
    component: getTemplate("blog/blog"),
})

Big difference being that I omitted matchPath altogether. As stated in the description, there are several issues with matchPath and this plugin that should be addressed first.

Edit: Excuse me, just read my own description more carefully. This PR uses matchPatch in order to match against languages, so named parameters for localized routes and matchPath functionality are mutually exclusive!

Got same issue and not quite understanding how to work it placed in gatsby-node.js

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

  if (page.path.match(/^\/request/)) {
    page.matchPath = "/request/*"

    createPage(page)
  }
}

Then in my request.js file place

<Router>
     <ErrorPage default component={ErrorPage}/>
     <Pagerequest path="/request/:id" />
</Router>

Still though not working at all as every time I pass something for :id it either shows 404 when I include en in the url and if I remove the en in the url it removes the :id

jojobagings78 avatar Jul 14 '20 21:07 jojobagings78

Again, you should not use matchPath at all. Just define routes with named parameters in the path attribute in the createPages hook, and do not define matchPath. That means the PR in its current state is not compatible with named parameters of a dynamic router, like the one you seem to be using in your example. This PR is intended for server side rendered routes, but allows for dynamic parameters on runtime.

Quick example:

// gatsby-node.js
exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions
   createPage({
      path: "/route/:param",
      component: path.resolve("./src/template/route.tsx")
  })
});

exports.onCreatePage = ({ page, actions: { createPage, deletePage } }) => {
  deletePage(page)
  createPage({
    ...page,
   // Whatever additional logic you might need here
  })
})

Component:

// src/template/route.tsx
interface MyRouteProps {
  param:string
}

const MyRoute: FC<MyRouteProps> = ({
  param 
}) => {
  // param is now equal to the :param parameter in the path
  // So for a route called /route/hello the prop param would be equal to 'hello'
});

Hless avatar Jul 15 '20 12:07 Hless

Again, you should not use matchPath at all. Just define routes with named parameters in the path attribute in the createPages hook, and do not define matchPath. That means the PR in its current state is not compatible with named parameters of a dynamic router, like the one you seem to be using in your example. This PR is intended for server side rendered routes, but allows for dynamic parameters on runtime.

Quick example:

// gatsby-node.js
exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions
   createPage({
      path: "/route/:param",
      component: path.resolve("./src/template/route.tsx")
  })
});

exports.onCreatePage = ({ page, actions: { createPage, deletePage } }) => {
  deletePage(page)
  createPage({
    ...page,
   // Whatever additional logic you might need here
  })
})

Component:

// src/template/route.tsx
interface MyRouteProps {
  param:string
}

const MyRoute: FC<MyRouteProps> = ({
  param 
}) => {
  // param is now equal to the :param parameter in the path
  // So for a route called /route/hello the prop param would be equal to 'hello'
});

Apart from it will return :param contains invalid WIN32 path characters.

Because you cant use :

jojobagings78 avatar Jul 15 '20 16:07 jojobagings78

Ah, that indeed looks like a bug for windows machines. Which is strange because Gatsby core supports this functionality: https://github.com/gatsbyjs/gatsby/issues/13965#issuecomment-617364363

Which version of Gatsby do you use?

It could be mitigated by replacing : with _ (or another character that is safe for windows paths) during the conversion of path to matchPath in gatsy-node: https://github.com/wiziple/gatsby-plugin-intl/blob/bf7fbbd054adce30de8e330d8d31608a1dc846f0/src/gatsby-node.js#L79

Feels kind of hacky though

Hless avatar Jul 15 '20 17:07 Hless

I am using Gatsby CLI version: 2.8.19 Gatsby version: 2.18.12

Tried using your example and just nothing 404 error page

jojobagings78 avatar Jul 15 '20 18:07 jojobagings78

Ah, that indeed looks like a bug for windows machines. Which is strange because Gatsby core supports this functionality: gatsbyjs/gatsby#13965 (comment)

Which version of Gatsby do you use?

It could be mitigated by replacing : with _ (or another character that is safe for windows paths) during the conversion of path to matchPath in gatsy-node: https://github.com/wiziple/gatsby-plugin-intl/blob/bf7fbbd054adce30de8e330d8d31608a1dc846f0/src/gatsby-node.js#L79

Feels kind of hacky though

Got it to work with but problem is I change from en to say fr and the actual language does not change and stays on en even though its fr in the url.

exports.onCreatePage = async ({ page, actions }) => {
  const { path, matchPath, context } = page;
  const { createPage, deletePage } = actions;

  // Build client only paths for account
  if (path.match(/^\/request2\//)) {
    const { intl } = context;

    deletePage(page);
    createPage({
      ...page,
      matchPath: `/:locale/request2/*`,
      context: {
        ...context,
        intl: {
          ...intl,
          routed: true, // prevents auto redirection on these routes
        },
      },
    });
  }
}
<Router>
      <ErrorPage default component={ErrorPage}/>
      <Pagerequest path="/:locale/request2/:identifier" />
</Router>

jojobagings78 avatar Jul 15 '20 19:07 jojobagings78

Ah, that indeed looks like a bug for windows machines. Which is strange because Gatsby core supports this functionality: gatsbyjs/gatsby#13965 (comment) Which version of Gatsby do you use? It could be mitigated by replacing : with _ (or another character that is safe for windows paths) during the conversion of path to matchPath in gatsy-node: https://github.com/wiziple/gatsby-plugin-intl/blob/bf7fbbd054adce30de8e330d8d31608a1dc846f0/src/gatsby-node.js#L79

Feels kind of hacky though

Got it to work with but problem is I change from en to say fr and the actual language does not change and stays on en even though its fr in the url.

exports.onCreatePage = async ({ page, actions }) => {
  const { path, matchPath, context } = page;
  const { createPage, deletePage } = actions;

  // Build client only paths for account
  if (path.match(/^\/request2\//)) {
    const { intl } = context;

    deletePage(page);
    createPage({
      ...page,
      matchPath: `/:locale/request2/*`,
      context: {
        ...context,
        intl: {
          ...intl,
          routed: true, // prevents auto redirection on these routes
        },
      },
    });
  }
}
<Router>
      <ErrorPage default component={ErrorPage}/>
      <Pagerequest path="/:locale/request2/:identifier" />
</Router>

Anyone got any clue ?

I was also able to get it to work but it was on en in url but showing in different language the last one in the array.

jojobagings78 avatar Jul 16 '20 13:07 jojobagings78

That is because that is still not the intended use case for this PR... I had the same problem using your code, that is why I made this PR. You are also not using the latest gatsby, might want to try that first?

But, the windows path problem made me realise that there is still work to do on this PR.

@jojobagings78 I would also like to kindly request not to post further problems with code that is not related to the workings of this pull request. You could try to open a different ticket for that.

Hless avatar Jul 16 '20 13:07 Hless

Just cant seem to find a solution which shows how under developed this library is and everyone seems to be as lost as me.

jojobagings78 avatar Jul 24 '20 22:07 jojobagings78

@jojobagings78 I've update this PR for my project and you could try it https://github.com/denplis/gatsby-plugin-intl/tree/matchpath

Installation: npm install denplis/gatsby-plugin-intl#matchpath --save

Example of usage:

actions.createPage({
    path: "/route",
    matchPath: "/route/:param",
    component: path.resolve("./src/template/route.tsx")
})

....

if (template === 'invitation') {
    page.matchPath = '/invitation/:inviteId';
}

denplis avatar Aug 31 '20 21:08 denplis

Thank you @Hless This fork fixed the issue where I had a page /order/:id and when the url for that page was set by a client without a locale prefix /en/order/:id/would lead to a 404

wiltsu avatar Mar 28 '21 16:03 wiltsu