React Router v4 Animated Transition Example

2.5k views Asked by At

The animation transition example provided in the v4 docs seems a little convoluted to me, since it depicts fading the same component in and out and adjusting the background color.

I'm trying to apply this technique to a more real-world example of fading one component out and another in, however I can't get it to work properly (it only seems to fade the first one out, then the second one pops in, and this transition only works one way (back button results in no transition).

Here's my code, which uses a stripped down version of MatchWithFade from the example:

import React from 'react';
import { TransitionMotion, spring } from 'react-motion'
import { HashRouter, Match, Miss } from 'react-router';
import Home from './components/Home';
import Player from './components/Player';
import FormConfirmation from './components/FormConfirmation';

const App = () => (
  <HashRouter>
    <div className="App">
      <MatchWithFade exactly pattern="/" component={Home} />

      <MatchWithFade pattern="/player/:playerId" component={Player} />

      <MatchWithFade pattern="/entered" component={FormConfirmation} />

      <Miss render={(props) => (
        <Home players={Players} prizes={Prizes} {...props} />
      )} />
    </div>
  </HashRouter>
);

const MatchWithFade = ({ component:Component, ...rest }) => {
  const willLeave = () => ({ zIndex: 1, opacity: spring(0) })

  return (
    <Match {...rest} children={({ matched, ...props }) => (
      <TransitionMotion
        willLeave={willLeave}
        styles={matched ? [ {
          key: props.location.pathname,
          style: { opacity: spring(1) },
          data: props
        } ] : []}
      >
        {interpolatedStyles => (
          <div>
            {interpolatedStyles.map(config => (
              <div
                key={config.key}
                style={{...config.style}}
              >
                <Component {...config.data}/>
              </div>
            ))}
          </div>
        )}
      </TransitionMotion>
    )}/>
  )
}

export default App;

I realize that this question is nearly a duplicate of this one, however that one has an accepted answer that doesn't actually answer the question.

4

There are 4 answers

1
Jayen On

The v4 docs have moved to react-transition-group so you may consider to do the same.

With regards to "fading the same component in and out" it's not really the same instance of the Component. Two instances exist simultaneously and can provide the cross-fade. react-motion docs says "TransitionMotion has kept c around" and I imagine react-transition-group is the same.

0
Ibrahim On

react-motion is not good for large applications. Use react-transition-group instead.

Because react-motion use javascript to perform the transition. When making API call the transition will cross path(sync call in js) and make the transition laggy when route to new page. Where as react-transition-group use CSS to perform the transition.

1
Radu Luncasu On

Here's an updated solution using react 16 and ReactCSSTransitionGroup from "react-addons-css-transition-group"

Edit zw0xv4vy3x

index.js

import React, { Component } from "react";
import ReactCSSTransitionGroup from "react-addons-css-transition-group";
import { render } from "react-dom";
import "./main.css";

function* continuosArrayIterator(arr) {
  let idx = 0;
  while (idx < arr.length) {
    let ret = arr[idx];
    idx++;
    if (idx === arr.length) {
      idx = 0;
    }
    yield ret;
  }
}

class App extends Component {
  constructor() {
    super();
    this.clickHandler = this.clickHandler.bind(this);
    this.items = [
      {
        id: 1,
        text: "item1",
        img: "https://mirrors.creativecommons.org/presskit/icons/cc.large.png"
      },
      {
        id: 2,
        text: "item2",
        img: "https://mirrors.creativecommons.org/presskit/icons/by.large.png"
      },
      {
        id: 3,
        text: "item3",
        img: "https://mirrors.creativecommons.org/presskit/icons/nc.large.png"
      },
      {
        id: 4,
        text: "item4",
        img:
          "https://mirrors.creativecommons.org/presskit/icons/nc-eu.large.png"
      }
    ];
    this.imageIterator = continuosArrayIterator(this.items);
    this.state = {
      image: this.imageIterator.next().value
    };
  }

  clickHandler(event) {
    return this.setState({
      image: this.imageIterator.next().value
    });
  }

  render() {
    return (
      <div>
        <button onClick={this.clickHandler}>Next Image</button>
        <ReactCSSTransitionGroup
          transitionAppear={true}
          transitionLeaveTimeout={500}
          transitionEnterTimeout={500}
          className="container"
          transitionName="example"
        >
          <div
            key={this.state.image.id}
            style={{
              position: "absolute",
              backgroundImage: `url(${this.state.image.img}`,
              backgroundSize: "auto 100px",
              height: "100px",
              width: "100px"
            }}
          />
        </ReactCSSTransitionGroup>
      </div>
    );
  }
}

render(<App />, document.getElementById("root"));

main.css

.container {
  position: absolute;
}

.example-enter {
  opacity: 0.01;
}

.example-enter.example-enter-active {
  opacity: 1;
  transition: opacity 500ms ease-in;
}

.example-leave {
  opacity: 1;
}

.example-leave.example-leave-active {
  opacity: 0.01;
  transition: opacity 500ms ease-in;
}

.example-appear {
  opacity: 0.01;
}

.example-appear.example-appear-active {
  opacity: 1;
  transition: opacity 0.5s ease-in;
}
0
Kevin Williams On

I didn't like the RRv4 example for animiations either. I ended up writing my own "AnimatedSwitch" for RRv4 Native.

I imagine it wouldn't take too much work to swap out the native animations for react-spring and be up and running.

https://javascriptrambling.blogspot.com/2020/07/react-router-native-animatedswitch.html