4

I've read this post, but I don't like having browserHistory.push('/some/path') in one of my components while I have <Route path="/some/path" component={SomePage} /> in my router file since the path is duplicated. What if I want to change that path to /another/path? Now I need to remember to update it in the router file and also my component.

Is there a better way around this? I was thinking that I could have "/some/path" and all my other paths defined in some constants file that gets imported and referenced in my router and my component. Example:

paths.js

var Paths = {
    myPath: "/some/path",
    ...
}
module.exports = Paths

router.jsx

var Paths = require('constants/paths');
...
<Route path={Paths.myPath} component={SomePage} />

component.jsx

var Paths = require('constants/paths');
...
browserhistory.push(Paths.myPath)

This seems like it could get a little messy when dealing with URL parameters like /some/path/:id, so I was hoping there might be a better way.

Community
  • 1
  • 1
mplis
  • 748
  • 2
  • 6
  • 19
  • What are you doing that you are pushing the history? Most basic apps don't need to worry about pushing anything onto history. – Matthew Herbst May 25 '16 at 20:59
  • 2
    @MatthewHerbst really? I can think of lots of reasons a basic app would do that.. like navigate after logging in for instance (result of ajax returning) – azium May 25 '16 at 21:03
  • I think a constants file sounds fine to me, and if you want to deal with parameters you could have a `buildPath(value)` type function inside your constants file. – azium May 25 '16 at 21:05
  • @azium different ideas of basic :) – Matthew Herbst May 25 '16 at 21:29
  • the router should *already* be doing that for you. You don't push manually, you tell the router to use a browserHistory as its `history` property, and *it* then takes care of the URL management whenever you navigate using `` components. You should have no reason to manually be doing this. – Mike 'Pomax' Kamermans May 25 '16 at 22:27
  • 2
    @Mike'Pomax'Kamermans what if the navigation doesn't occur on clicking a `` There are *so* many reasons why that might be the case, such as auth which I already mentioned – azium May 25 '16 at 22:42
  • You don't need the user to *click* a `Link` to use one. You can trigger it without a user ever seeing it, or clicking it, by simply triggering it yourself in code. Set up the `Link`, make it invisible however you like, and then when the navigation needs to occur, you trigger it, done. – Mike 'Pomax' Kamermans May 25 '16 at 23:35
  • @Mike'Pomax'Kamermans that seems a lot more awkward to me than calling `browserHistory` which ships with react-router.. not to mention OP didn't specify *where* `.push` was being called, could very well be outside of a component. that's why this exists https://github.com/reactjs/react-router/blob/master/docs/guides/NavigatingOutsideOfComponents.md – azium May 26 '16 at 00:39
  • Sorry OP for all this confusion. Doesn't matter if you're using `Link` or `.push` your question remains the same. I hope my original comment helps out (using a path building function) – azium May 26 '16 at 00:42

1 Answers1

0

This is what I have done in the past for routing to make it simpler / more streamlined.

(as a side note i used lodash here so if you aren't you can use native functions to do basically the same thing. lodash just adds a bunch of nice features / functions that you dont need to go write yourself)

in my routes.jsx file you should create functions that convert any parameters into a url with a default path for this answer lets just make one for a profile route

export function pathToProfile(userName, params) {
  return path(Paths.PROFILE, _.assign({userName}, params));
}

the path() function is just a simple helper utility function for generating a path.

path(url, params, urlMap) {
  if(!url) {
    console.error("URL is not defined for action: ", params);
  }
  if(!params)
    return url;
  params = _.merge({}, params);
  if(urlMap) {
    _.keys(urlMap).forEach((k) => {
      params[urlMap[k]] = params[k];
      delete params[k];
    });
  }
  if(url.indexOf(":") !== -1) {
    url.match(/:([0-9_a-z]+)/gi).forEach((match)=>{
      var key = match.replace(":", "");
      url = url.replace(match, params[key]);
      params = _.omit(params, key);
    });
  }
  if(_.keys(params).length > 0) {
    url = url + "?" + this.paramsToString(params);
  }
  return url;
}

now looking at the constants file

Paths {
  PROFILE: '/user/:username',
}

Finally usage.

I wouldn't recommend broswerHistory.push() when you can have an onClick handler. Yes it works and will redirect, but is it the best thing to use? react-router also has a Link that should be used wherever possible. you get some nice additional features one for example would be an active route for whatever page you're on.

browserHistory.push() is a good way to handle redirecting when you do things like an auth login redirect or if you are responding to data from a request and conditionally taking them to a page.

<Route path={Paths.PROFILE} component={Profile} />

<Link to={pathToProfile(this.props.user.username, {myParams: 'here'})></Link>

what that would be translated into if you wanted to see the url from that exact link it would be /user/someUsername?myParams=here

John Ruddell
  • 25,283
  • 6
  • 57
  • 86
  • Why not `push`? How would you switch routes as a result of an ajax response for example? – azium May 26 '16 at 03:05
  • @azium if you read my answer completely you will see that I say use it when you can to get the benefits like an active route... but I said there are cases where you would need to do a push – John Ruddell May 26 '16 at 03:09
  • 1
    There are a bunch of things you get with a link that you don't with a push.. open in new tab, active route so it can be styled.. etc. The recommended practice is if it's an element that does the routing make it a link. That's what it's designed for. But the push is needed for the cases when you need to do it programmatically – John Ruddell May 26 '16 at 03:59