Content Source:
- medium.com
Let’s describe the problem first, we’ll discuss about the solution with some example code to cover it in this article.
React-router and react-transition-group are two widely used libraries that can be combined to create transitions between routes.
However, in react-transition-group, once a component is mounted, its exit animation is specified, not changeable. Thus, dealing with transitions depending of the next state (what we call dynamic transitions) is challenging with this library.
it is not easy to tweak it to create more sophisticated use cases such as dynamic transitions. In this article, we’ll explain how to do so thanks to react-router v4 and react-transition-group v2.
If you are on your way developing transitions between pages of your react app, you might have already met this code snippet. (adapted with state A/B):
<TransitionGroup> <CSSTransition key={location.key} classNames="fade" timeout={300}> <Switch location={location}> <Route exact path="/state-a" component={A} /> <Route exact path="/state-b" component={B} /> </Switch> </CSSTransition> </TransitionGroup>
Understanding why this piece of code allows a transition between two routes is not obvious. However, it is necessary to implement a more sophisticated use case such as dynamic transitions.
About TransitionGroup
When transitioning from state A to state B, location.key
value changes (let’s say from A
to B
) so without a <TransitionGroup>
wrapping <CSSTransition key={location.key}>
, the <CSSTransition key='A'>
would be unmounted and a new <CSSTransition key='B'>
would be mounted (because react identifies elements thanks to key).
However, <TransitionGroup>
tracks its children by key and when one of its children disappears, it keeps rendering it for the time of the transition. So during the time of the transition, the above TransitionGroup would render something similar to this:
<div> <CSSTransition key='A' leaving> <Switch location={location}> <Route exact path="/state-a" component={A} /> <Route exact path="/state-b" component={B} /> </Switch> </CSSTransition> <CSSTransition key='B' entering> <Switch location={location}> <Route exact path="/state-a" component={A} /> <Route exact path="/state-b" component={B} /> </Switch> </CSSTransition> </div>
About Switch
You simply need to understand that when location.pathname
is /state-a
, this:
<Switch> <Route exact path="/state-a" component={A} /> <Route exact path="/state-b" component={B} /> </Switch>
it renders:
<A />
Why you need to pass a location prop to the switch?
By default, a switch uses history.location
to select the route to render. However you can provide a location prop to the switch
that will override the default history.location
value:
<TransitionGroup> <CSSTransition key={location.key} classNames="fade" timeout={300}> <Switch location={location}> <Route exact path="/state-a" component={A} /> <Route exact path="/state-b" component={B} /> </Switch> </CSSTransition> </TransitionGroup>
So why this location
(provided by withRouter
or available within a route component) must be added as a prop to the switch in a transition use case?
history.location
is a live object whereas the location
provided by withRouter
is immutable. Thus, without providing a location
prop to the switch, the switch would always match the route according to the current location (the location of history.location
). So during the transition (the current location is B
), the <TransitionGroup>
would render:
<div> <CSSTransition key='A' leaving> <B /> </CSSTransition> <CSSTransition key='B' entering> <B /> </CSSTransition> </div>
However, if you pass a location to the switch, the switch will use this prop instead of history.location
and since location
is immutable, the previous <CSSTransition>
received the previous location
and the new <CSSTransition>
receives the new location
.
<div> <CSSTransition key='A' leaving> <A /> </CSSTransition> <CSSTransition key='B' entering> <B /> </CSSTransition> </div>
Thereby the leaving <CSSTransition>
will still render an old route even if a new location has been pushed to the history.
Dealing with dynamic transitions is not straight forward. An issue on react-transition-group is open to consider this problem.
As explained in the issue:
once a component is mounted, its exit animation is specified, not changeable.
Indeed, in this code snippet:
<TransitionGroup> <CSSTransition key={location.key} classNames="fade" timeout={300}> <Switch location={location}> <Route exact path="/state-a" component={A} /> <Route exact path="/state-b" component={B} /> </Switch> </CSSTransition> </TransitionGroup>
only the current (entering) child is accessible. The exiting one has already been removed. It is only living within the <TransitionGroup>
state.
Fortunately the <TransitionGroup>
component can receive a childFactory
.
So the childFactory
prop makes it possible to specify the leaving transition of a component after rendering it (and thus solves the problem of dynamic transitions)
<TransitionGroup childFactory={child => React.cloneElement( child, {classNames: "newTransition", timeout: newTimeout} )} > <CSSTransition key={location.key}> <Switch location={location}> <Route exact path="/state-a" component={A} /> <Route exact path="/state-b" component={B} /> </Switch> </CSSTransition> </TransitionGroup>
In the above code snippet, the previous <CSSTransition>
will be updated with the new transition class name and timeout.
The question is now: how do you give the right classNames value according to the state transition?
A possible solution is to use the location state.
Here is how you could do:
// state-a.js export default (props) => ( <div> <button onClick={() => { history.push({ pathname: '/state-b', state: {transition: 'fade', duration: 300} }) }} >Go to state B</button> <button onClick={() => { history.push({ pathname: '/state-c', state: {transition: 'slide', duration: 500} }) }} >Go to state C</button> </div> )
In the routes definition file:
<TransitionGroup childFactory={child => React.cloneElement( child, { classNames: location.state.transition, timeout: location.state.duration } )} > <CSSTransition key={location.key}> <Switch location={location}> <Route exact path="/state-a" component={A} /> <Route exact path="/state-b" component={B} /> </Switch> </CSSTransition> </TransitionGroup>
Now you should get 2 different transitions from the same exiting state. This is what we were trying to solve.
For more Information and to build web app using React.js, Hire React.js Developer from us as we provide you high quality services by utilizing all the latest tools and advanced technology. E-mail us any clock at – hello@hkinfosoft.com or Skype us: “hkinfosoft“.
To develop custom web app using React.js, please visit our technology page.
Content Source:
57 Sherway St,
Stoney Creek, ON
L8J 0J3
606, Suvas Scala,
S P Ring Road, Nikol,
Ahmedabad 380049
1131 Baycrest Drive,
Wesley Chapel,
FL 33544
57 Sherway St,
Stoney Creek, ON
L8J 0J3
606, Suvas Scala,
S P Ring Road, Nikol,
Ahmedabad 380049
1131 Baycrest Drive,
Wesley Chapel,
FL 33544
© 2024 — HK Infosoft. All Rights Reserved.
© 2024 — HK Infosoft. All Rights Reserved.
T&C | Privacy Policy | Sitemap