8

I am using react-transition-group in a ReactJS and GatsbyJS (V2) project.

I have my page transitions working with animation but when Link exit, the exiting animation is cut short because the next page is ready for entering.

I have tried delaying the Link action but whilst the page change was delayed, the exit animation is not triggered until the delay was over and the Link was actioned.

How can I delay the page change, whilst initiating the exiting animation onClick? Alternatively, is there a better way or props available?

Here is my code

Layout.js

class Layout extends React.Component {
  ...
  return (
    <Transition>{children}</Transition>
  );
}

Transition.js

class Transition extends React.Component {
constructor(props) {
    super(props);
    this.state = { exiting: false };
    this.listenerHandler = this.listenerHandler.bind(this);
  }

  listenerHandler() {
    this.setState({ exiting: true });
  }

  componentDidMount() {
    window.addEventListener(historyExitingEventType, this.listenerHandler);
  }

  componentWillUnmount() {
    window.removeEventListener(historyExitingEventType, this.listenerHandler);
  }

  static getDerivedStateFromProps({ exiting }) {
    if (exiting) {
      return { exiting: false };
    }
    return null;
  }

  render() {
    const transitionProps = {
      timeout: {
        enter: 0,
        exit: timeout
      },
      appear: true,
      in: !this.state.exiting
    };

    return (
      <ReactTransition {...transitionProps}>
        {status => (
          <div
            style={{
              ...getTransitionStyle({ status, timeout })
            }}
          >
            {this.props.children}
          </div>
        )}
      </ReactTransition>
    );
  }
}

export default Transition;

gatsby-config.js

import createHistory from 'history/createBrowserHistory';

const timeout = 1500;
const historyExitingEventType = `history::exiting`;

const getUserConfirmation = (pathname, callback) => {
  const event = new CustomEvent(historyExitingEventType, {
    detail: { pathname }
  });
  window.dispatchEvent(event);
  setTimeout(() => {
    callback(true);
  }, timeout);
};

let history;
if (typeof document !== 'undefined') {
  history = createHistory({ getUserConfirmation });
  history.block(location => location.pathname);
}

export const replaceHistory = () => history;

export { historyExitingEventType, timeout };

getTransitionStyle.js

const getTransitionStyles = timeout => {

return {
    entering: {
      transform: `scale(1.05) translateZ(0)`,
      opacity: 0
    },
    entered: {
      transition: `transform 750ms ease, opacity ${timeout}ms ease`,
      transitionDelay: `750ms`,
      transform: `scale(1) translateZ(0)`,
      opacity: 1
    },
    exiting: {
      transition: `transform 750ms ease, opacity ${timeout}ms ease`,
      transform: `scale(0.98) translateZ(0)`,
      opacity: 0
    }
  };
};

const getTransitionStyle = ({ timeout, status }) =>
  getTransitionStyles(timeout)[status];

export default getTransitionStyle;
Darren
  • 2,176
  • 9
  • 42
  • 98
  • If you're on the latest Gatsby beta this won't work because Gatsby is now using Reach Router instead of react-router. Delaying route transitions isn't supported (yet). I left a comment on this [here](https://github.com/gatsbyjs/gatsby/issues/5656#issuecomment-411762437). – Fabian Schultz Aug 10 '18 at 13:46
  • Thanks @FabianSchultz. I saw your comment yesterday at the link provided and will look out for a response to the topic. Thank you again - Good to know that someone of your stature in the Gatsby community is looking for a solution. – Darren Aug 11 '18 at 08:21
  • 1
    @FabianSchultz. I noticed that you have added a solution on the **Gatsby** github that works perfectly to my issue. Thank you for that. I thought if you wanted to answer this question with that solution as to close it, I'll send the bounty your way. Cheers. – Darren Aug 23 '18 at 16:39

1 Answers1

4

Gatsby v2 is using Reach Router instead of React Router, so using getUserConfirmation with replaceHistory will not work anymore. In the Gatsby v2 RC you can use react-pose to create page transitions a little more straightforward:

gatsby-browser.js and gatsby-ssr.js:

import React from "react"
import Transition from "./src/components/transition"
 export const wrapPageElement = ({ element, props }) => {
  return <Transition {...props}>{element}</Transition>
}

transition.js component:

import React from "react"
import posed, { PoseGroup } from "react-pose"

const timeout = 250

class Transition extends React.PureComponent {
  render() {
    const { children, location } = this.props

    const RoutesContainer = posed.div({
      enter: { delay: timeout, delayChildren: timeout },
    })

    // To enable page transitions on mount / initial load,
    // use the prop `animateOnMount={true}` on `PoseGroup`.
    return (
      <PoseGroup>
        <RoutesContainer key={location.pathname}>{children}</RoutesContainer>
      </PoseGroup>
    )
  }
}

export default Transition

Inside your pages:

// Use `posed.div` elements anywhere on the pages.

const Transition = posed.div({
  enter: {
    opacity: 1,
  },
  exit: {
    opacity: 0,
  },
})

// ...

<Transition>Hello World!</Transition>

Check the official example for a working demo.

Fabian Schultz
  • 18,138
  • 5
  • 49
  • 56
  • Thanks Fabian. It is a great example. Accepted the answer obviously but how do I apply the bounty? It's generally available when I accept an answer. – Darren Aug 23 '18 at 17:51
  • I didn't see anything about the question having a bounty. No worries though! – Fabian Schultz Aug 23 '18 at 17:53
  • Looking further the bounty expired, sorry. Was a good one too at 150 - would have put you over the 7k. Anyway, cheers and have a great day. – Darren Aug 23 '18 at 17:57