Skip to content

how to set dynamic animations? #182

@jasonintju

Description

@jasonintju

I am using react-transition-group v2.2.0 and react-router v4.1.1 to do the page sliding animation. Once a component is mounted, its exit animation is specified, not changeable, which troubles me.

function getPathDepth(location) {
  let pathArr = (location || {}).pathname.split('/');
  pathArr = pathArr.filter(n => n !== '');
  return pathArr.length;
}

class Routers extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.state = {
      prevDepth: getPathDepth(props.location)
    };
  }

  componentWillReceiveProps() {
    console.log('prev:', getPathDepth(this.props.location));
    this.setState({ prevDepth: getPathDepth(this.props.location) });
  }

  render() {
    return (
      <Route render={({ location }) => 
          <TransitionGroup>
            <CSSTransition
              key={location.key}
              timeout={500}
              classNames={ getPathDepth(location) - this.state.prevDepth > 0 ? 'pageSliderLeft' : 'pageSliderRight' }
              classNames={classNames}
              mountOnEnter={true}
              unmountOnExit={true}
            >
              <Switch location={location}>
                <Route path="/" exact component={ Index } />
                <Route path="/comments" component={ Comments } />
                <Route path="/opinions" component={ Opinions } />
                <Route path="/games/lol" component={ LOL } />
                <Route path="/games/dota" component={ DotA } />
                <Route path="/games" component={ Games } />
              </Switch>
            </CSSTransition>
          </TransitionGroup>
      } />
    )
  }
}

const WrapRouters = withRouter(Routers);

export default function RouterMap() {
  return (
    <BrowserRouter>
      <WrapRouters />
    </BrowserRouter>
  );
}

The css:

.pageSliderRight-enter {
  transform: translate3d(-100%, 0, 0);
}
.pageSliderRight-enter.pageSliderRight-enter-active {
  transform: translate3d(0, 0, 0);
  transition: all 600ms;
}
.pageSliderRight-exit {
  transform: translate3d(0, 0, 0);
}
.pageSliderRight-exit.pageSliderRight-exit-active {
  transform: translate3d(-100%, 0, 0);
  transition: all 600ms;
}

.pageSliderLeft-enter {
  transform: translate3d(100%, 0, 0);
}
.pageSliderLeft-enter.pageSliderLeft-enter-active {
  transform: translate3d(0, 0, 0);
  transition: all 600ms;
}
.pageSliderLeft-exit {
  transform: translate3d(0, 0, 0);
}
.pageSliderLeft-exit.pageSliderLeft-exit-active {
  transform: translate3d(100%, 0, 0);
  transition: all 600ms;
}

The animation:

pageslidinganimation3

If only sliding from '/' to '/games' and then from '/games' back to '/', everything is fine. But with more routes, it gets complicated.

For the Games component:

  • type 1: '/' --> '/games' && '/games' --> '/', exit animation should be 'slide to right'
  • type 2: '/' --> '/games' && '/games' -->'/games/lol', exit animation should be 'slide to left'

As the Games component mounted, its exit animation classNames is 'pageSliderLeft' or 'pageSliderRight'. No matter which class, there is only one animation. But Games component has two exit types.

How to make the Games component's exiting show different animations?

Activity

Chopinsky

Chopinsky commented on Aug 31, 2017

@Chopinsky

This is a typical unmounting animation problem, I've run into it before. My solution back then was to call setState to update the classNames first, such that the exit animation will be up to date, and then set a timer with 0 delay to update the in states to trigger the exit animation. Not pretty, but working.

jquense

jquense commented on Aug 31, 2017

@jquense
Collaborator

yeah the issue is that you can't really change the animations on an item from the outside the "normal" way because you unrender it. There is the childFactory prop for addressing this, which is called with the item and the state even for the items you don't "see" anymore

m-allanson

m-allanson commented on Oct 6, 2017

@m-allanson
Contributor

For anyone else that finds their way here - there's a great Q&A on StackOverflow with a detailed explanation on how to use childFactory: https://stackoverflow.com/questions/41404232/react-transitiongroup-and-react-cloneelement-do-not-send-updated-props

pmcalmeida

pmcalmeida commented on Nov 1, 2017

@pmcalmeida

@Chopinsky I'm not sure I follow. Could you paste an example demonstrating how you achieved that?

cjnaude

cjnaude commented on Dec 5, 2017

@cjnaude

childFactory is definitely the way to go. Thanks to the SO link provided by @m-allanson, I was able to use the childFactory as follows to correctly update the animation:

//This returns a childFactory to provide to TransitionGroup
const childFactoryCreator = (classNames) => (
  (child) => (
    React.cloneElement(child, {
      classNames
    })
  )
);

<TransitionGroup
  childFactory={childFactoryCreator(condition ? "anim1" : "anim2)}
>
  <CSSTransition
     key={key}
     classNames={condition ? "anim1" : "anim2"}
  >
     //Item that gets transitioned goes here
  </CSSTransition>
</TransitionGroup>
marshall-cho

marshall-cho commented on Feb 27, 2018

@marshall-cho

@cjnaude Does this really work with React 16 and React Transition Group v2?

  1. Personnally I couldn't get it to work.
  2. The SO answer is from Dec 31 '16.
TapX

TapX commented on Feb 28, 2018

@TapX

@marshall-cho Here are my dependencies:
"react": "^16.2.0"
"react-transition-group": "^2.2.1"

Are you getting any specific errors, or is the animation just not updating?

nicgirault

nicgirault commented on Mar 8, 2018

@nicgirault

I just wanted to share an article I just wrote dealing with this issue: https://medium.com/lalilo/dynamic-transitions-with-react-router-and-react-transition-group-69ab795815c9. This thread helped me a lot. Thanks

sag1v

sag1v commented on Mar 16, 2018

@sag1v

@nicgirault Greate article.
The part of providing the immutable location to the switch made me smile 😄
I'm going to explore your example's source code, it looks great!
Thank you.

moneydance

moneydance commented on Jan 30, 2019

@moneydance

https://codesandbox.io/s/p93vp612w0 Heres a sandbox app with my implementation if anyones struggling with this.

johnbonds

johnbonds commented on Oct 30, 2020

@johnbonds

If anyone else is struggling with this problem, the way we fixed it was to wrap the entire inside a div. Then when we want the components to slide in either direction we dynamically change the class of that parent div which targets (selects) the classes of the child elements inside the transition component. That way the classes inside the transition stay static while the classes of parent div change thereby controlling the direction of the transitions using CSS selectors.

cjke

cjke commented on Nov 20, 2020

@cjke

@nicgirault Thanks for the article mate, I was really struggling to get this right

chupzzz

chupzzz commented on May 19, 2022

@chupzzz

2022 and the issue is still here!

Solved by example of @moneydance (comment above): thanks a lot, you saved my day!

One-string solver:

const dynamicChildFactory = classNames => child => React.cloneElement(child, { classNames });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @jquense@m-allanson@silvenon@nicgirault@cjke

        Issue actions

          how to set dynamic animations? · Issue #182 · reactjs/react-transition-group