Skip to content

Delegate route matching logic from <Switch> to <Route> (feature request) #5546

@SleepWalker

Description

@SleepWalker

Hello,

currently you have following logic in <Switch> component:

https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Switch.js#L41-L60

<Switch> component makes assumption, that it will always receive the 'whatever' component, but with the specific options (path, exact, etc).

What the bad here is that if you'll want to wrap <Route> component in HOC with a different props interface, this new <HocRoute> won't work with <Switch> anymore, because <Switch> has its own copy-pasted matching logic and it, actually routes and only then switches.

What I want to propose is to delegate routing logic to <Route> components only. E.g. by creating static computeMatch() method and by calling element.type.computeMatch(props, router) instead of calling matchPath() on props, that do not belong to <Switch> component.

I know, that element.type is a little bit hacky, but this will allow a great composability and extendability of react-router.

The use cases

Named routes

You can implement something like this:

<NamedRoute name="home" component={HomePage} />
import { Route } from 'react-router-dom';

const routes = {
  home: '/'
};

class NamedRoute extends Component // or it can be extends Route too {
  static computeMatch({ name, ...rest }, router) {
    const path = routes[name] || '/';

    return Route.computeMatch({ path, ...rest }, router);
  }

  render() {
    return <Route {...this.props} />;
  }
}

Route aliasing

<Route aliases={['/', '/index']} component={HomePage} />
import { Route, Switch } from 'react-router-dom';

class AliasRoute extends Component {
  static computeMatch({ aliases, ...rest }, router) {
    // btw. the alias can be the whole object
    // with props for Route instead of only path
    return aliases.reduce((match, path) => {
      if (match) {
        return match;
      }

      return Route.computeMatch({ path, ...rest }, router);
    }, null);
  }

  render() {
    const { aliases, ...rest } = this.props;

    return (
      <Switch>
        {aliases.map((path) => (
          <Route path={path} {...rest} />
        ))}
      </Switch>
    );
  }
}

This component can actually solve another <Switch> nesting problem, but the actual <Switch> component is hidden inside <AliasRoute>.

Switch nesting

The <Switch> can actually implement computeMatch() too and allow nesting:

  static computeMatch({ children, location }, router) {
    let match = null;

    React.Children.forEach(children, (child) => {
      if (match) {
        return true;
      }

      match = child.type.computeMatch({location, ...child.props}, router);
    });

    return match;
  }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions