React-spring: Usage with react-router v4

Created on 25 Mar 2018  路  9Comments  路  Source: pmndrs/react-spring

This library looks awesome!

Has this been tested with react-router v4 for route based transitions? It would be awesome if the docs included an example for this use case!

Most helpful comment

Happy with that one or need more?

https://codesandbox.io/embed/xo0lrqw2nz

All 9 comments

@mjsisley hey, thanks a lot! For sure it will work with React-router. I will make an example for it. Anything you look for specifically or just a plain transition between routes?

Happy with that one or need more?

https://codesandbox.io/embed/xo0lrqw2nz

Thanks for the example! I'm looking forward to implementing this as I prefer it to Animated's imperative approach.

I realize Issues shouldn't be used for support questions, but it seems relevant to this particular issue. I'm trying to create an AnimatedSwitch component that I can re-use across multiple "Layouts" in my app. I'm not sure how to pass the style prop to the children prop. Is this is a valid approach?

import { Switch, withRouter } from 'react-router-dom';
import { Transition, config } from 'react-spring';

const AnimatedSwitch = ({ children, location }) => (
  <div className="content">
    <Transition
      config={config.slow}
      keys={location.pathname}
      from={{ opacity: 0, transform: 'scale3d(0.5,0.5,0.5)' }}
      enter={{ opacity: 1, transform: 'scale3d(1,1,1)' }}
      leave={{ opacity: 0, transform: 'scale3d(0.5,0.5,0.5)' }}
    >
      {style => (
        <Switch location={location}>
          {children}
        </Switch>
      )}
    </Transition>
  </div>
);

AnimatedSwitch.propTypes = {
  location: PropTypes.shape({
    pathname: PropTypes.string.isRequired,
  }).isRequired,
  children: PropTypes.node.isRequired,
};

export default withRouter(AnimatedSwitch);

Usage would look something like:

const Public = () => (
  <div>
    <AnimatedSwitch>
      <Route path="/auth/login" render={props => Login({...props, style })} />
      <Route path="/auth/signup" render={props => Signup({...props, style })} />
    </AnimatedSwitch>
  </div>
);

Passing props down like that is hard with react-router. There's been this issue: https://github.com/ReactTraining/react-router/issues/4105#issuecomment-291834881 but for your use-case none of the examples would would, not even cloning. I've struggled with similar problems in the past. The only way i see would be allowing AnimatedSwitch itself to call a render child into which it can pass "styles", but that wouldn't be any different from how it is right now.

I'd probably leave Transition as it is, use it whenever it has to be used, but perhaps inject its props so that it becomes short to use, like so:

const props = {
    config: config.slow,
    from: { opacity: 0, transform: 'scale3d(0.5,0.5,0.5)' },
    enter: { opacity: 1, transform: 'scale3d(1,1,1)' },
    leave: { opacity: 0, transform: 'scale3d(0.5,0.5,0.5)' },
}

<Transition {...props} keys={location.pathname}>
    {styles => (
        <Switch location={location}>
            <Route path="/auth/login" render={props => Login({...props, style })} />
        <Route path="/auth/signup" render={props => Signup({...props, style })} />
        </Switch>
    )}
</Transition>

@drcmda Thanks a bunch for adding these examples! They cover the use cases I had in mind.

Hi, will this also work for react-router v3?

The spring is pretty forward in what it does, it simply moves props (or styles) from here to there. It should work with any react component.

@dylinmaust @drcmda I implemented <AnimatedSwitch /> like this :

const AnimatedSwitch = withRouter(_AnimatedSwitch)

function _AnimatedSwitch({ location, children, ...transitionProps }) {
  return (
    <Transition native keys={location.pathname} {...transitionProps}>
      {style => (
        <Switch>
          {React.Children.map(children, child => {
            const { component: Component, ...childProps } = child.props
            const AnimatedComponent = animated(Component)
            return React.createElement(child.type, {
              ...childProps,
              render: props => <AnimatedComponent {...{ ...props, style }} />
            })
          })}
        </Switch>
      )}
    </Transition>
  )
}

Usage :

<AnimatedSwitch
  from={{ transform: 'translateY(100px)', opacity: 0 }}
  enter={{ transform: 'translateY(0px)', opacity: 1 }}
  leave={{ transform: 'translateY(100px)', opacity: 0 }}
>
  <Route
    exact
    path="/page-1"
    component={Page1}
  />
  <Route exact path="/page-2" component={Page2} />
</AnimatedSwitch>

It seems to work so far. Do you see anything wrong?

Doing a search for reach router points to here as well, so I will leave a comment with a possible solution

import React from 'react';
import { Location, Router } from '@reach/router';
import { Transition } from 'react-spring/renderprops';
import styled from 'styled-components';

const StyledRouter = styled(Router)`
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
`;

function FadeInRouter({ children }) {
  return (
    <Location>
      {({ location }) => (
        <Transition
          items={location}
          keys={(location) => location.key}
          from={{ opacity: 0 }}
          enter={{ opacity: 1 }}
          leave={{ opacity: 0 }}
        >
          {(location) => (props) => (
            <StyledRouter location={location} style={props}>
              {children}
            </StyledRouter>
          )}
        </Transition>
      )}
    </Location>
  );
}

export default FadeInRouter;

and later you can use this router

<FadeInRouter>
  <ContactUs path="contact-us" />
  <About path="about />
</FadeInRouter>
Was this page helpful?
0 / 5 - 0 ratings