Jimmy
Jimmy

Reputation: 3860

Component cannot listen to react-router

I have a component that is used persistently across my spa. I want it to be aware of my router and the various paths that my spa is on. Is there an easy way to do this, or do I have to bandaid some redux (or something similar) state solution that is always listening to my router changes? Thanks! You can see the below for an example.

index.jsx:

import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'connected-react-router';
import { Route, Switch } from 'react-router-dom';
import { history, store } from './redux/store';
import Navigation from './navigation';

const UserReport = () => <h2>User Report</h2>;

const UserPage = () => <h2>User Page</h2>;

const Routes = () => (
  <React.Fragment>
    <Route component={Navigation} />
    <Switch>
      <Route exact path="/users/:startDate" component={UserReport} />
      <Route exact path="/users/:userId" component={UserPage} />
    </Switch>
  </React.Fragment>
);

render(
  <Provider store={store}>
    <ConnectedRouter history={history}>
      <Routes />
    </ConnectedRouter>
  </Provider>, document.getElementById('app'),
);

navigation.jsx:

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

const Navigation = (props) => {
  console.log(props.match.path);
  // expected: "/users/:startDate"
  // received: "/"
  return (
    <h2>Navigation</h2>
  );
};

export default withRouter(Navigation);

Upvotes: 1

Views: 2408

Answers (2)

Jimmy
Jimmy

Reputation: 3860

I ended up using this react-router github discussion as my solution.

An example of my implementation:

index.jsx:

import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'connected-react-router';
import { Route, Switch } from 'react-router-dom';
import { history, store } from './redux/store';
import Layout from './layout';

const home = () => <h2>Home Page</h2>;
const users = () => <h2>Users</h2>;
const userPage = () => <h2>User Page</h2>;

const layoutRender = component => route => <Layout component={component} route={route} />;

const Routes = () => (
  <Switch>
    <Route exact path="/" component={layoutRender(home)} />
    <Route exact path="/users" component={layoutRender(users)} />
    <Route exact path="/users/:id" component={layoutRender(userPage)} />
  </Switch>
);

render(
  <Provider store={store}>
    <ConnectedRouter history={history}>
      <Routes />
    </ConnectedRouter>
  </Provider>, document.getElementById('app'),
);

layout.jsx:

import React from 'react';

const Layout = (props) => {
  const {
    component: Component,
    route,
  } = props;

  return (
    <div>
      <h1>This is the layout</h1>
      <Component route={route} />
    </div>
  );
};

export default Layout;

Upvotes: 0

Sam R.
Sam R.

Reputation: 16450

Since the Navigation route doesn't have any path specified, it always matches whatever path you're on but the match.path only shows you the minimum path required to match for the navigation. That's why it's always /.

You can use location.pathname but it gives you the matched value and not the matched path.

const Navigation = props => {
  console.log(props.location.pathname);
  // prints `/users/1` if you're on https://blah.com/users/1
  // prints `/users/hey` if you're on https://blah.com/users/hey
  return <h2>Navigation</h2>;
};

Not sure that's what you want but if you expand what exactly you're trying to achieve, maybe I can help more.

Moreover, your second route to path="/users/:userId" overshadows the first route. Meaning there is no way to tell if hey in /users/hey is startDate or userId. You should introduce a separate route like path="/users/page/:userId".

Upvotes: 1

Related Questions