Scotty Compton
Scotty Compton

Reputation: 43

How to properly implement React Transition Group for Page Transitions

I've been tearing my hair out all day trying to get this to work. What I'm noticing is that for whatever reason, the transition classes (classNames="fade") never get applied to the page elements. So when I navigate from one page to another, for a brief period (the timeout period) both of the page components will be displayed.

What I should have for 600ms...

<div class="RTG"> 
    <div class="page fade-appear fade-enter fade-enter-active"> <!-- 
destination component HTML --></div>
    <div class="page fade-exit fade-exit-active"> <!-- start component HTML --></div>
</div>

What I get is..

<div class="RTG">
   <!-- "fade..." classes never applied to the child nodes" -->
   <div class="page"> <!-- destination component HTML --></div>
   <div class="page"> <!-- start component HTML --></div>
</div>

And then after the 600ms timeout, I'm left with...

<div class="RTG">
   <div class="page"> <!-- destination component HTML --></div>
</div>

NOTE 1: I put the "RTG" className on the TransitionGroup component simply to verify that my "page" class components are actually direct descendants of the TransitionGroup component. Doesn't exist for any other reason.

NOTE 2: I'm using [email protected] because I have a compatibility issue with the latest version.

AppRouter.js

import PrivateRoute from './PrivateRoute';
import PublicRoute from './PublicRoute';
import { CSSTransition, TransitionGroup } from 'react-transition-group'



const AppRouter = () => (
    <Router history={history}>
        <Route render={({location}) => {
            return (
                <TransitionGroup className="RTG">
                <CSSTransition 
                    key={location.key}
                    timeout={600}
                    classNames="fade"
                >
                    <Switch location={location}>
                        <PublicRoute path="/" component={LoginPage} exact={true} />
                        <PrivateRoute path="/dashboard" component={ExpenseDashboardPage} />
                        <PrivateRoute path="/create" component={AddExpensePage} />
                        <PrivateRoute path="/edit/:id" component={EditExpensePage} />
                        <Route component={NotFoundPage} />
                    </Switch>
                </CSSTransition>
           </TransitionGroup>
            );
        }} />

    </Router>
);


export default AppRouter;

PrivateRoute.js

export const PrivateRoute = ({ 
    isAuthenticated, 
    component: Component,
    ...rest
}) => (
    <Route {...rest} component={(props) => (
        isAuthenticated ? (
            <div className="page">
                <Header />
                <Component {...props} />
            </div>
        ) : (
            <Redirect to="/" />
        )
    )} />
);

const mapStateToProps = (state) => ({
    isAuthenticated: !!state.auth.uid
});

export default connect(mapStateToProps)(PrivateRoute);

PublicRoute.js

export const PublicRoute = ({ 
    isAuthenticated, 
    component: Component,
    ...rest
}) => (
    <Route {...rest} component={(props) => (
        isAuthenticated ? (
            <Redirect to="/dashboard" />
        ) : (
            <div class="page">
                <Component {...props} />
            </div>
        )
    )} />
);

const mapStateToProps = (state) => ({
    isAuthenticated: !!state.auth.uid
});

export default connect(mapStateToProps)(PublicRoute);

Applicable CSS Styles


.page {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
}

.fade-appear,
.fade-enter {
    opacity: 0;
    z-index: 1;
}

.fade-appear-active,
.fade-enter.fade-enter-active {
    opacity: 1.0;
    transition: opacity 300ms linear 150ms;
}

.fade-exit {
    opacity: 1.0;
}

.fade-exit.fade-exit-active {
    opacity: 0;
    transition: opacity 150ms linear;
}

Upvotes: 1

Views: 3929

Answers (2)

user10645790
user10645790

Reputation: 1274

Making fluid beautiful full page transitions can be tricky.

Most solutions implement it on the route component.

Link Approach

Define the animations on a custom Link component. When user navigate, the next page will enter, and the current one will leave.

<Link
  to="/some-path"
  transition="glide-right"
/>

Of course, for that you must create your own link...

So you can use packages that does that, like react-tiger-transition.

Demo

demo-gif

(I'm the author)

Upvotes: 2

Chris Chen
Chris Chen

Reputation: 1293

Apparently you will need to wrap <Switch> inside a parent component.

CSSTransition works as it will try to inject classNames to its children as props. Which means the child element will need to take these props and pass on as classNames. Thus you will need to wrap <Switch> into another component to make sure this mechanism works properly.

Edit on CodeSandbox

const AppRouter = () => (
  <Router>
    <Route
      render={({ location }) => {
        return (
          <TransitionGroup className="RTG">
            <CSSTransition key={location.key} timeout={600} classNames="fade">
              <div>
                <Switch location={location}>
                  <PublicRoute path="/" component={LoginPage} exact={true} />
                  <PrivateRoute
                    path="/dashboard"
                    component={ExpenseDashboardPage}
                  />
                  <PrivateRoute path="/create" component={AddExpensePage} />
                  <PrivateRoute path="/edit/:id" component={EditExpensePage} />
                  <Route component={NotFoundPage} />
                </Switch>
              </div>
            </CSSTransition>
          </TransitionGroup>
        );
      }}
    />
  </Router>
);

Upvotes: 5

Related Questions