23

I have a set of dynamically parameters in the URL, ad example the user locale, that looks like that:

/en/homepage

In my router config JSON file, I have something like:

/:locale/homepage

Which is the best way in order to replace these parameters directly in React Router?

I've come up with this solution that looks to me very far away from a standard or portable solution:

const urlTemplate = '/:language/homepage';
const mappedUrl = pathToRegexp.compile(urlTemplate);
const url = mappedUrl({
  'language': this.props.match.params.language
})
this.props.history.push(url);

I'm able to retrieve the match parameters if the component is wrapped by withRouter ( HOC from React router ) and replace them with pathToRegexp ( pillarjs/path-to-regexp ), but if I need to use <Link to={}> in a stateless component it's very annoying.

Are there standard solutions in order to do that?

Thank you in advance :)

Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
AnnamariaL
  • 251
  • 1
  • 2
  • 6
  • You would need to use withRouter if the component is not receiving Router props, other possible solution is to pass all the props down to the child component – Shubham Khatri Dec 22 '17 at 17:25
  • @ShubhamKhatri Thank you for the answer. The problem is if have a long tail of nested stateless components it becomes bit complex :) – AnnamariaL Dec 22 '17 at 17:53
  • If you have a long trail of nested stateless components you may be interested in knowing about React Context Api https://reactjs.org/blog/2018/03/29/react-v-16-3.html#official-context-api – Rohith Murali May 19 '18 at 12:33
  • If you have a long trail of nested stateless components you may be interested in knowing about [React Context Api][1] [1]w: https://reactjs.org/blog/2018/03/29/react-v-16-3.html#official-context-api – Rohith Murali May 19 '18 at 12:36
  • @AnnamariaL did any of the answers help you with your issue? do you need more alternatives to solve your problem? – c-chavez Oct 27 '18 at 09:17

5 Answers5

56

I used React Router's generatePath to update the path /map/:zoom/:lon/:lat/ when a user changes a Leaflet map view. Note that in my case I replaced the path rather than pushing it.

import { generatePath } from 'react-router';

function updatePath(lat, lon, zoom) {
  const path = generatePath(this.props.match.path, { lat, lon, zoom });
  this.props.history.replace(path);
}
Leila Hadj-Chikh
  • 1,653
  • 17
  • 16
  • 2
    The only correct answer, IMHO, because it does not provide any "bicycles" and shows native method to work with routes in ReactRouter 4+ – Limbo Mar 29 '19 at 09:43
  • Yes this is the right answer. Isn't there an open source lib around for that? I always struggle to find this code back (years ago I even created my own code but it's hard to support all edge cases correctly) React Router v6 link: https://reactrouter.com/en/main/utils/generate-path – Eric Burel Sep 01 '22 at 06:48
2

I use a function that replaces the parameters of the route path with the arguments of the function using regex.

If a route is:

'/:language/homepage'

the function will replace :language with the parameter passed to the function.

Function

This is the function to replace the values in the path:

function getRoute(path) {
    let args = Array.prototype.slice.call(arguments, 1);
    let count = -1;
    return path.replace(/:[a-zA-Z?]+/g, function (match) {
        count += 1;
        return args[count] !== undefined ? args[count] : match;
    });
};

The key here is the regex /:[a-zA-Z?]+/g that only match strings that start with : and include any character between a-z and A-Z meaning that when the next / is found, it will finish the match, so it will replace all params defined like this.

This works for my specific usage of simple params, but if you need to add more characters to the matching regex you can do so (like numbers, underscores, etc).

Usage

It is used like this:

let newPath = getRoute('/:language/homepage', 'en');

and the result will be:

'/en/homepage'

Link example

It can be used for Links like this:

<Link to={ getRoute('/:language/homepage', 'en') }>

Multiple parameters in route

It accepts multiple parameters as well:

getRoute('/:id/:language/homepage/:value', 123, 'en', 'myVal')

this will return:

'/123/en/homepage/myVal'
c-chavez
  • 7,237
  • 5
  • 35
  • 49
1

React Router Dom params replacing function. Benefits:

  • unlimited amount of parameters
  • flexible for other libraries via optional prefix parameter
  • handles both relatives and absolute urls

const repalacePathParams = (path, params, prefix = ':') => {
  let newPath = path

  Object.entries(params).forEach(([key, value]) => {
    newPath = newPath.replace(prefix + key, value)
  })
  return newPath
}

console.log(repalacePathParams('/profile/:id/:name', { id: '111', name: 'tim' })) 
//return '/profile/111/tim'
rottitime
  • 1,653
  • 17
  • 29
0

Well, not sure you still interested in answer, and also not sure that my answer will help you, but...

If your paths are simple such as in your example (I mean, they don't include regexps, such as /:locale/kappa([a-z]{2})), you can write this router-independent function, where you manually handle your path and replace parameters :)

function placeParams(pathRegex, params) {
  var segments = pathRegex.split("/")
  return segments.map(segment => {
    var offset = segment.indexOf(":") + 1
    if (!offset)
      return segment

    var key = segment.slice(offset)
    return params[key]
  }).join("/")
}

console.log(placeParams("/:locale/kappa", { locale: "en" }))
Limbo
  • 2,123
  • 21
  • 41
0

This is an improved answer that allow you to place optional parameters also.

function placeParams(pathRegex, params) {
  const segments = pathRegex.split("/");
  return (
    segments
    .map((segment) => {
      let offset = segment.indexOf(":?") + 1;

      if (offset) {
        let key = segment.slice(offset + 1);
        return params[key];
      }

      offset = segment.indexOf(":") + 1;
      if (!offset) return segment;

      let key = segment.slice(offset);
      return params[key];
    })
    .join("/")
    // Remove trailing "/"
    .replace(/\/+$/, "")
    // Remove redundant "/"
    .replace(/\/{2,}/, "/")
  );
}

console.log(placeParams("/:locale/user/:?name/:?lastname", {
  locale: "en",
  name: "Nady",
  lastname: "Shalaby"
}))
Nady Shalaby
  • 604
  • 7
  • 7