13

In my app, I'd like to match both the path and the hash to different components. For example:

/pageA#modalB

Would show PageA as the main page, with modalB over the top. I've tried the following, with many variations of the path property:

<Route path="#modalB" component={modalB}/>

But nothing works.

In React Router 2 inside a modal 'controller' component, I would use:

browserHistory.listen( (location) => { //do something with loction.hash })

I was hoping for something a little more elegant in V4

rje
  • 390
  • 2
  • 8

3 Answers3

17

Not out of the box, but the beauty of React Router 4 is that it's incredibly easy to implement this yourself.

let HashRoute = ({ component: Component, path, ...routeProps }) => (
  <Route 
    {...routeProps}
    component={({ location, ...props }) =>
      location.hash === path && <Component {...props} />
    }
  />
)

<HashRoute path="#modalB" component={ModalB} />
azium
  • 20,056
  • 7
  • 57
  • 79
  • Very clean. I also go it working by using withRouter. https://reacttraining.com/react-router/#withrouter – rje Feb 04 '17 at 08:47
  • If you are using any weird char in your hash, you have to decodeURI(location.hash) or else it won't match properly. – localhost Apr 19 '18 at 14:05
6

@azium answer works fine as long as you don't need to use render or children prop in HashRoute. In which case this solution will work better :

import React from 'react';
import { Route } from 'react-router-dom';

const HashRoute = ({ hash, ...routeProps }) => (
  <Route
    render={({ location }) => (
      (location.hash === hash) && <Route {...routeProps} />
    )}
  />
);

export default HashRoute;

Use it like that :

<HashRoute hash="#modalB" component={ModalB} />

Or combine it with route matching :

<HashRoute hash="#modalB" path="/subPageOnly" component={ModalB} />
Poyoman
  • 1,652
  • 1
  • 20
  • 28
1

if you actually want to match and get the parameters, use matchPath.

import { useLocation, matchPath } from 'react-router-dom';

// your route you want to see if it matches
const routePath = '/overtherainbow/:country/#/city/:city/detail'

// somewhere while rendering
const location = useLocation();
useEffect(() => {
  const matched = matchPath(location.pathname + location.hash, routePath);
  if (matched){
    // matched, do something with it, like setting state, fetching data or what not
    console.log(matched.params); // will be {country:..., city:...}
  }
}, [location])
Steve Ladavich
  • 3,472
  • 20
  • 27
japrescott
  • 4,736
  • 3
  • 25
  • 37
  • So the match algorithm can deal with `#`, it's just that react-router just defaults to using `location.pathname` alone? Is it important to have slashes before and/or after the `#`? Would say `'/overtherainbow/:country#:city/detail` work too? – Beni Cherniavsky-Paskin Dec 28 '21 at 22:27
  • yes that should work - any path you want (limited by the placeholders like `:country` as outlined in the react-router docs) – japrescott Jan 03 '22 at 11:29