I am implementing a slider for components rendered by the router. Created a component with TransitionGroup to automate appending of the classes by CSSTransition, and the code can be seen in a sandbox here. I have added 8 classes (2x enter, enter active, exit and exit active) to have left-right and right-left behavior ("Next" and "Back" buttons).
App.css file:
.slideIn-enter {
opacity: 0;
transform: translateX(-500px);
}
.slideIn-enter-active {
opacity: 1;
transition: all 600ms ease-out;
transform: translateX(0);
}
.slideIn-exit {
opacity: 1;
}
.slideIn-exit-active {
opacity: 0;
transition: all 600ms ease-in;
transform: translateX(500px);
}
/* */
.slideOut-enter {
opacity: 0;
transform: translateX(500px);
}
.slideOut-enter-active {
opacity: 1;
transition: all 600ms ease-in-out;
transform: translateX(0);
}
.slideOut-exit {
opacity: 1;
}
.slideOut-exit-active {
opacity: 0;
transition: all 600ms ease;
transform: translateX(-500px);
}
Slide component which renders the component that is routed to:
import React from 'react';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import { useLocation } from 'react-router-dom';
import './App.css';
export default function Slide(props) {
const location = useLocation();
return (
<>
<div className="box-container">
<TransitionGroup component={null}>
<CSSTransition
in={props.visible}
timeout={1200}
appear
classNames={props.isForward ? 'slideIn' : 'slideOut'}
unmountOnExit
key={location.key}
>
{props.children}
</CSSTransition>
</TransitionGroup>
</div>
</>
);
}
And App.js where the Slide component is wrapping a Switch which renders one of 2 routes with each press of the 2 buttons below:
import React, { useState } from 'react';
import Slide from './Slide';
import { Route, Switch } from 'react-router-dom';
import { useHistory } from 'react-router-dom';
import './App.css';
export default function App() {
const [isForward, setIsForward] = useState(true);
const history = useHistory();
const BoxRed = () => {
return <div className="box1">Box1</div>;
};
const BoxGreen = () => {
return <div className="box2">Box2</div>;
};
const pushNewRoute = () => {
if (history.location.pathname.endsWith('red')) {
history.push('/green');
} else {
history.push('/red');
}
};
return (
<>
<Route
render={({ location }) => (
<Slide isForward={isForward}>
<Switch location={location}>
<Route path="/green" component={BoxRed} />
<Route path="/red" component={BoxGreen} />
</Switch>
</Slide>
)}
></Route>
<div>
<button
className="btn-container"
onClick={() => {
setIsForward(true);
pushNewRoute();
}}
>
Next
</button>
<button
className="btn-container"
onClick={() => {
setIsForward(false);
pushNewRoute();
}}
>
Back
</button>
</div>
</>
);
}
In my index.js, I am wrapping the App.js in a BrowserRouter:
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Route } from 'react-router-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<BrowserRouter>
<Route path="/" component={App} />
</BrowserRouter>,
document.getElementById('root')
);
reportWebVitals();
Main issue, that can be seen in the codebox, is that the component stops when exiting component is in exit-active state (lets call it state) and the entering component is in enter-active state.
When entering component is set to enter-done, seems like it is translated half of its width more in the direction defined by the CSS and I cannot figure out why. I presumed the timeout on TransitionGroup was causing it, but whatever value greater than the transition time in CSS classes I'd put in, it just delayed the effect and cannot figure out why.
There is also a smaller issue with switching sets of classes which can be observed when going "Next" then "Back", but feel free to ignore it unless the issue is obvious (probably CSS classes aren't removed when appending new ones, but haven't had the time to tackle it). Thank you in advance!