Skip to content

a variant of <Switch> that's much easier to use with transition components

License

Notifications You must be signed in to change notification settings

jcoreio/react-router-transition-switch

Repository files navigation

react-router-transition-switch

CircleCI Coverage Status semantic-release Commitizen friendly npm version

This is a variant of <Switch> that's much easier to use with transition components and solves some problems.

The current recommended transition approach for react-router is

import { Route, Switch } from 'react-router-dom'
import Fader from 'react-fader'

const MyRoute = () => (
  <Route
    render={({ location }) => (
      <Fader>
        <Switch key={location.key} location={location}>
          <Route path="/red" component={Red} />
          <Route path="/green" component={Green} />
          <Route path="/blue" component={Blue} />
        </Switch>
      </Fader>
    )}
  />
)

This has several problems:

  1. All <Switch>es transition on every location change, even if:
    • only the last part of the URL changed and you only want the innermost nested <Switch> to transition
    • you have the same component for two different paths and don't want to transition that component
    • you don't want to transition in some case for any other reason
  2. You have to pass a location to the <Switch> for it to work

react-router-transition-switch simplifies the above example to

import { Route } from 'react-router-dom'
import Switch from 'react-router-transition-switch'
import Fader from 'react-fader'

const MyRoute = () => (
  <Switch component={Fader}>
    <Route path="/red" component={Red} />
    <Route path="/green" component={Green} />
    <Route path="/blue" component={Blue} />
  </Switch>
)

Differences from react-router's <Switch>:

  1. You can pass it a component or render prop. It will use them to wrap the matched <Route> if given
  2. By default it clones the matched <Route> with key={match.url} unless you gave the <Route> a key yourself. This way the Fader will only perform a transition when:
    • if you provide keys yourself, the matched <Route> has a different key than the last
    • otherwise, the matched portion of the location is different from the last`
  3. You can pass it a createKey prop, which is a function taking the (route, match) and returning the key to use.

component example

import React from 'react'
import { BrowserRouter as Router, Route } from 'react-router-dom'
import Fader from 'react-fader'
import Switch from 'react-router-transition-switch'

// ...
const MyRoute = () => (
  <Router>
    <Switch component={Fader}>
      <Route exact path="/" component={Home} />
      <Route path="/about" component={About} />
      <Route path="/account" component={Account} />
      <Route path="/users/:userId" component={User} />
    </Switch>
  </Router>
)

For the location /users/andy/profile, the <Switch> will render:

<Fader>
  <Route key="/users/andy/profile" path="/users/:userId" component={User} />
</Fader>

Notice that it makes match.url the key of the matched <Route>, so that <Fader> (or whatever transition component you use) knows to perform a transition. If you provide custom keys on the <Route>s you pass to <Switch>, it won't overwrite them.

render example

As with <Route>, you may pass a render function instead of a component:

<Router>
  <Switch
    render={({ children }) => (
      <ReactCSSTransitionGroup
        transitionName="example"
        transitionEnterTimeout={300}
        transitionLeaveTimeout={300}
      >
        {children}
      </ReactCSSTransitionGroup>
    )}
  >
    ...
  </Switch>
</Router>

Preventing transitions in certain cases

If you want to prevent transitions between certain <Route>s, give them the same key. This will not cause problems because <Switch> only renders one of the child <Route>s it was passed, so there will never be duplicate keys during React's reconciliation step.

<Router>
  <Switch component={Fader}>
    <Route key="home" exact path="/" component={Home} />
    <Route key="orders" exact path="/orders" component={Orders} />
    <Route key="orders" path="/orders/:orderId" component={Orders} />
    <Route key="about" path="/about" component={About} />
  </Switch>
</Router>

Forcing transitions in certain cases

If you have to pass in an array of <Route>s, they will already have keys, hence changes between subroutes will not transition since react-router-transition-switch does not override existing keys with the match.url.

In this case, you can use the createKey prop to force a unique key for every match:

<Router>
  <Switch component={Fader} createKey={(child, match) => match.url}>
    {routes}
  </Switch>
</Router>