remix-run / react-router

Declarative routing for React
https://reactrouter.com
MIT License
53.16k stars 10.31k forks source link

Feature request -- Working together with React Transition Group (low level API) #5269

Closed Bram-Zijp closed 7 years ago

Bram-Zijp commented 7 years ago

At it's current state, React Router v4 will not work together with React Transition Group low level API for animated transitions on page navigation. I fiddled around, asked the question on multiple places without any answers. The only workaround I found was writing a custom switch which requires allot more boilerplate. Therefor I would like to request implemented functionality and documentation so that other people their JS fatigue remains leveled avoiding lots of fiddling.

Codepen Stackoverflow URL

uxlayouts commented 7 years ago

I agree. I'm using react-router-config and loving it but it's so hard to just do below (Click first example): react-css-transition-replace Demo

I have tried: react-easy-transition throws propType errors and new page loads before old page can disappear react-router-transition Position absolute is pretty gross when components collapse react-css-transition-replace Looks promising but I can't get the structure right. (Used in example below) react-motion is great but documentation is not focused on routing or RR4 for example and the boilerplate is a lot to take in.

I would like the cross fade with animated height Demo Here: react-css-transition-replace Demo

Related RR4 + react-css-transition-replace Thread: https://github.com/marnusw/react-css-transition-replace/issues/46

Here is my code:

App.js page:

import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import { renderRoutes } from 'react-router-config';
import routes from './routes';

const App = () => {
  return (
    <Router>
      {renderRoutes(routes)}
    </Router>
  );
}

export default App;

Base.js page (Base class inside App.js):

import React from 'react';
import { renderRoutes } from 'react-router-config';
import ReactCSSTransitionReplace from 'react-css-transition-replace';
import { shape } from 'prop-types';
import Header from './Header';
import './Base.css';
import './styles/includes.css';

const Base = ({route, location}, {router}) => (
  <div className="App">
    <Header />
    <ReactCSSTransitionReplace
      transitionName="cross-fade"
      transitionEnterTimeout={1000}
      transitionLeaveTimeout={1000}
    >
      {renderRoutes(route.routes, { key: location.pathname,})}
    </ReactCSSTransitionReplace>
  </div>
);

Base.propTypes = {
  location: shape({}),
};

export default Base;")

And by routes.js page:

import Base from './Base';
import Home from './Home';
import About from './About';
import NotFound from './NotFound';
import Change from './Change';
import OurTeam from './OurTeam';
import Tacos from './Tacos';
import Chicken from './Chicken';
import Veggie from './Veggie';
import Posts from './Posts';
import Post from './Post';

export default [
  { component: Base,
    routes: [
      { path: '/',
        exact: true,
        component: Home,
      },
      {
        path: '/about',
        component: About,
        name: 'About',
      },
      {
        path: '/change',
        component: Change,
        name: 'Change',
      },
      {
        path: '/our-team',
        component: OurTeam,
        name: 'OurTeam',
      },
      {
        path: '/tacos',
        component: Tacos,
        routes: [
          { path: '/tacos/chicken',
            component: Chicken,
          },
          { path: '/tacos/veggie',
            component: Veggie,
          }
        ]
      },
      {
        path: '/posts',
        exact: true,
        component: Posts,
        name: 'Posts',
      },
      {
        path: '/posts/:id',
        component: Post
      },
      {
        component: NotFound,
      },
    ]
  },
]

Css file:

.cross-fade-leave {
  opacity: 1;
}
.cross-fade-leave.cross-fade-leave-active {
  opacity: 0;
  transition: opacity 1s ease-in;
}

.cross-fade-enter {
  opacity: 0;
}
.cross-fade-enter.cross-fade-enter-active {
  opacity: 1;
  transition: opacity 1s ease-in;
}

.cross-fade-height {
  transition: height .5s ease-in-out;
}
phyrog commented 7 years ago

Perhaps this could be fixed by giving Switch an optional parameter component that is rendered around the matching Route component.

<Switch component={ReactCSSTransitionGroup}>
  <Route path="/foo" component={Foo} key="foo" />
  <Route path="/bar" component={Bar} key="bar" />
</Switch>

would render e.g.

<ReactCSSTransitionGroup>
  <Foo key="foo" {...} />
</ReactCSSTransitionGroup>

instead of directly rendering <Foo {...} /> as it does now.

Since only the inner component changes and not ReactCSSTransitionGroup, that should work, no?

timdorr commented 7 years ago

Just follow these guidelines: https://github.com/ReactTraining/react-router/issues/4351#issuecomment-281198187

phyrog commented 7 years ago

@timdorr that looks more like a hack than an actual solution, but fine by me, as long as it works :smile:

Bram-Zijp commented 7 years ago

@timdorr Doesn't seem to work following those guidelines? This also doesn't seem to work

cristiandley commented 7 years ago

@timdorr #4351 (comment) does not solve the issue... it works yes (but no transition effect is shown). I tried and asked everywhere about everything... i found no solution to this.

uxlayouts commented 7 years ago

Agree, switch is a hack and does not help if you are trying to keep your code clean and readable with react-router-config.

This is close but needs major refactoring to be as clean as it should be.

https://hackernoon.com/animated-page-transitions-with-react-router-4-reacttransitiongroup-and-animated-1ca17bd97a1a