Skip to content

[Proposal] router switch conditionals #6767

@ghost

Description

Router Switch Conditionals

What is

Sometimes we may wish to have a better way of controlling if a route is going to be rendered or not, without a redirect (as the wiki proposes). With this we can pass a conditional to a route as a prop so the switch may see if the route can render.

To this day work-around

While we don't have this resource, what I usually do, is pass a function to the path parameter of the route (inside a switch), an example as follows:

var user = null;

function isGuest(route) {
   return user === null? route:"impossible route";
}

export default function App (props) {
   return (
       <Switch>
              <Route path={isGuest("actual/route")} component={Component} />
       </Switch>
   );
}

What I'm doing is rendering an impossible route if the condition inside the function is not met, with this I can generate tons of functions to describe those conditions, such as: isGuest, isLogged, isAdmin. Of course, that this method is not ideal.

Sample

How the router switch conditionals would work:

var user = null;

export default function App (props) {
   return (
       <Switch>
              <Route path="actual/route" condition={user === null} component={Component} />
       </Switch>
   );
}

Returning true would allow the switch to consider this route when choosing which one to use.

Changes

The changes in the source code would be near to minimal

Switch class

class Switch extends React.Component {
  render() {
    return (
      <RouterContext.Consumer>
        {context => {
          invariant(context, "You should not use <Switch> outside a <Router>");

          const location = this.props.location || context.location;

          let element, match;

          // We use React.Children.forEach instead of React.Children.toArray().find()
          // here because toArray adds keys to all child elements and we do not want
          // to trigger an unmount/remount for two <Route>s that render the same
          // component at different URLs.
          React.Children.forEach(this.props.children, child => {
            if (match == null && React.isValidElement(child)) {
              element = child;

              const path = child.props.path || child.props.from;
              const cond = child.props.condition || true;

              match = (path || cond)
                ? matchPath(location.pathname, { ...child.props, path })
                : context.match;
            }
          });

          return match
            ? React.cloneElement(element, { location, computedMatch: match })
            : null;
        }}
      </RouterContext.Consumer>
    );
  }
}

The only thing that we are doing is adding a const that reads the condition and passing it for the match test altogether with the route comparison.

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature-requestUsed to close PRs that haven't gone through/been accepted the Proposal process yet

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions