I have a component that switches child components inside its ReactCSSTransitionGroup when its prop stage changes.
When the page loads the transitionAppear works fine but there is no transition leave or transition enter animation for when the prop changes / when the component inside ReactCSSTransitionGroup changes. When the stage changes from 'CONTENT' to 'BOOKING' the Content component should fade out while Booking component fades in.
Page component:
import React, {PropTypes} from 'react';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
export default class Page extends React.Component {
static propTypes = { stage: PropTypes.string };
getComponent(stage) {
switch (stage) {
case 'CONTENT': {
return (
<Content />
);
}
case 'BOOKING': {
return (
<Booking />
);
}
}
}
shouldComponentUpdate(nextProps) {
return nextProps.stage !== this.props.stage;
}
render() {
return (
<div>
<ReactCSSTransitionGroup
transitionName='component-fade'
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}
transitionEnterTimeout={500}
transitionAppearTimeout={500}
transitionLeaveTimeout={500}
component='div'
>
{this.getComponent(this.props.stage)}
</ReactCSSTransitionGroup>
</div>
);
}
}
CSS:
.component-fade-enter {
opacity: 0;
transform: translateY(+2em);
}
.component-fade-enter.component-fade-enter-active {
opacity: 1;
transform: translateY(0em);
transition: opacity 200ms ease-out, transform 200ms ease-in;
}
.component-fade-leave {
transform: translateY(0em);
opacity: 1;
}
.component-fade-leave.component-fade-leave-active {
transform: translateY(+2em);
opacity: 0;
transition: opacity 200ms ease-in, transform 200ms ease-in;
}
.component-fade-appear {
opacity: 0;
transform: translateY(+2em);
}
.component-fade-appear.component-fade-appear-active {
opacity: 1;
transform: translateY(0em);
transition: opacity 200ms ease-out, transform 200ms ease-in;
}
Figured out answer to my own question, just had to add a div wrapper to the components whose key changes as this.props.stage changes
<ReactCSSTransitionGroup
transitionName='component-fade'
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}
transitionEnterTimeout={500}
transitionAppearTimeout={500}
transitionLeaveTimeout={500}
component='div'
>
<div key={this.props.stage}>
{this.getComponent(this.props.stage)}
</div>
</ReactCSSTransitionGroup>
Related
I have a slide in animation that I want to kick in everytime one of the dots in the carousel is clicked. However with my current code the state is always true and if I set it to a "toggling" state, it will be "true,false" meaning the class will only be applied every one click of the dots (when is true).
I want the slide in effect to apply on every dot that is clicked. Code here:
JSX
const [onClick, setClick] = useState(false);
const handleDotClick = () => {
setClick(onClick => !onClick);
};
<div className={classnames(styles.grid, { [styles.slideIn]: onClick })}>
{data[0].map(({ image }) => {
return (
<div>
<Image src={`image.jpg`}/>
</div>
);
})}
</div>
<div className={styles.dots}>
{Array.apply(null, { length: 10 }).map((e, i) => (
<span onClick={handleDotClick}></span>
))}
</div>
CSS
.slideIn {
-webkit-animation: slideIn 0.5s forwards;
-moz-animation: slideIn 0.5s forwards;
animation: slideIn 0.5s forwards;
}
#-webkit-keyframes slideIn {
0% {
transform: translateX(900px);
}
100% {
transform: translateX(0);
}
}
#-moz-keyframes slideIn {
0% {
transform: translateX(900px);
}
100% {
transform: translateX(0);
}
}
#keyframes slideIn {
0% {
transform: translateX(900px);
}
100% {
transform: translateX(0);
}
}
Any idea on how to apply the slideIn class everytime the dot is clicked?
As per my understanding here is an example using useRef to apply the slideIn animation on each time a dot is clicked.
const ref = useRef();
const handleDotClick = () => {
if (ref.current) {
requestAnimationFrame(() => {
ref.current.classList.remove("slideIn");
});
requestAnimationFrame(() => {
ref.current.classList.add("slideIn");
});
}
};
<div ref={ref} className={classnames(styles.grid, { [styles.slideIn]: onClick })}>
{data[0].map(({ image }) => {
return (
<div>
<Image src={`image.jpg`} />
</div>
);
})}
</div>
<div className={styles.dots}>
{Array.apply(null, { length: 10 }).map((e, i) => (
<span onClick={handleDotClick}></span>
))}
</div>
I made a toggle function that calls from the button and i dont understand why the Jobs component who receives the styles appears suddenly without without transitioning.
I want that the opacity.
I want the opacity to increase until the component displays slowly.
import React, { Component } from 'react';
import { withTranslation } from 'react-i18next';
import './Experience.css';
class Experience extends Component {
constructor() {
super()
this.state = { display: 'none', opacity: 0 }
this.toggle = this.toggle.bind(this)
}
toggle() {
console.log("ENTRO EN TOOGLE")
if (this.state.display === 'none') {
this.setState({ display: 'block' })
setTimeout(() =>
this.setState({ opacity: 1 }), 300
)
}
else {
this.setState({ opacity: 0 })
setTimeout(() =>
this.setState({ display: 'none' }), 900
)
}
}
styles = (e) => {
console.log(e)
}
render() {
const styles = this.state;
const { t } = this.props;
return (
<div>
<div className="card">
<div className="card-content">
<div className="row mt-top">
<div className="col xl4 l4 m6 s12">
<button onClick={this.toggle}>DESPLEGAR</button>
</div>
<Jobs style={styles}></Jobs>
</div>
</div>
</div>
</div>
);
}
}
export default withTranslation()(Experience);
what you're attempting is like wanting to call a function without calling it when you want an element to transition you need to call the transition CSS property
something like this.
.name {
opacity: 0;
transition: opacity 0.5s ease;
}
.name:hover {
opacity: 1;
}
I'm trying to integrate CSSTransition to my Gatsby site, but it is not applying any of the classes. I'm utilizing CSS modules, and I've got a <div> that serves as the parent that fades in and out, essentially applying the fade effect to this and covering the content while it changes. It's got the class fadEffect. Here is my app-layout component, and the SASS.
AppLayout.tsx
import React, { ReactNode, useState } from 'react';
import { ApiContext } from 'contexts/ApiContext';
import { graphql, StaticQuery } from 'gatsby';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import { Devtools } from '../devtools/Devtools';
import { Footer } from '../footer/Footer';
import { Header } from '../header/Header';
import s from './AppLayout.scss';
interface AppLayoutProps {
children: ReactNode;
location: string;
}
const isDev = process.env.NODE_ENV === 'development';
// tslint:disable no-default-export
export default ({ children, location }: AppLayoutProps) => {
const [fadeEffectVisible, setFadeEffectVisible] = useState(false);
const handleFadeEffectEntered = () => {
setTimeout(() => {
setFadeEffectVisible(false);
}, 50);
};
return (
<StaticQuery
query={`${NavQuery}`}
render={(data) => (
<>
<ApiContext>
<Header navigationContent={data.prismic.allNavigations.edges[0].node} />
<CSSTransition
in={fadeEffectVisible}
timeout={150}
classNames={{
enter: s.fadeEffectEnter,
enterActive: s.fadeEffectEnterActive,
enterDone: s.fadeEffectEnterDone,
exit: s.fadeEffectExit,
exitActive: s.fadeEffectExitActive,
}}
onEntered={handleFadeEffectEntered}
>
<div className={s.fadeEffect} aria-hidden="true" />
</CSSTransition>
<TransitionGroup component={null}>
<CSSTransition
key={location}
timeout={150}
classNames={{
enter: s.pageEnter,
}}
>
<div className={s.layout}>
{children}
<Footer navigationItems={data.prismic.allNavigations.edges[0].node} />
{isDev && <Devtools />}
</div>
</CSSTransition>
</TransitionGroup>
</ApiContext>
</>
)}
/>
);
};
const NavQuery = graphql`
query NavQuery {
prismic {
allNavigations {
edges {
node {
...NotificationBar
...NavigationItems
...FooterNavigationItems
}
}
}
}
}
`;
AppLayout.scss
#import '~styles/config';
:global {
#import '~styles/base';
}
.layout {
display: block;
min-height: 100vh;
}
.pageEnter {
display: none;
}
.fadeEffect {
display: none;
position: fixed;
z-index: 9;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #fff;
transition: opacity 0.15s linear;
&Enter {
display: block;
opacity: 0;
}
&Active,
&Done,
&Exit {
display: block;
opacity: 1;
}
&ExitActive {
opacity: 0;
}
}
I'm happy to provide more details/code if this isn't enough. I'm newish to React and Gatsby, so I'm still learning the lingo. Thanks in advance.
I don't see part of your code where you are updating fadeEffectVisible to true for first CSSTransition and I don't see in property at all on second CSSTransition and I would bet that is your issue. Please take a look at this example from React Transition Group for understanding usage of properties.
App.js
function App() {
const [inProp, setInProp] = useState(false);
return (
<div>
<CSSTransition in={inProp} timeout={200} classNames="my-node">
<div>
{"I'll receive my-node-* classes"}
</div>
</CSSTransition>
<button type="button" onClick={() => setInProp(true)}>
Click to Enter
</button>
</div>
);
}
Style.css
.my-node-enter {
opacity: 0;
}
.my-node-enter-active {
opacity: 1;
transition: opacity 200ms;
}
.my-node-exit {
opacity: 1;
}
.my-node-exit-active {
opacity: 0;
transition: opacity 200ms;
}
When the in prop is set to true, the child component will first receive the class example-enter, then the example-enter-active will be added in the next tick.
I need some help because I can't seem to understand what I'm doing wrong...
Bellow is my render method from my component and the css used. What I'm trying to do is while the user is not authenticated to see a splash screen instead of the router.
It works but the problem is that there is no fadeIn effect when showing the splash component (mounting), but there is a fadeOut fadeIn effect when transitioning from the splash screen to the router...
render
render = () => {
let { user } = this.props;
return (
<TransitionGroup id='app' enter={true}>
{user.authenticated && <CSSTransition
in={true}
classNames='fade'
key={'router'}
timeout={{ enter: duration, exit: duration }}
>
<Router />
</CSSTransition>}
{!user.authenticated && <CSSTransition
in={true}
classNames='fade'
key={'splash'}
timeout={{ enter: duration, exit: duration }}
>
<Splash />
</CSSTransition>}
</TransitionGroup>
);
}
css
.fade-enter {
opacity: 0.01;
}
.fade-enter.fade-enter-active {
opacity: 1;
transition: opacity 500ms ease-in;
}
.fade-exit {
opacity: 1;
}
.fade-exit.fade-exit-active {
opacity: 0.01;
transition: opacity 500ms ease-in;
}
Looking at the component docs, react components has three items in their lifecycle: mount, unmount and update. React transition groups seems to be the most common way to apply transitions and animations in react. Can it be used on update aswell, (ie statechange) or only when an item is mounted/unmounted?
Yes you can add transition on state change. You need to provide a key to the child element which will change on state update.
From docs :
You must provide the key attribute for all children of ReactCSSTransitionGroup, even when only rendering a single item. This is how React will determine which children have entered, left, or stayed.
Thus, you can do something like this :
class Container extends React.Component {
constructor(props) {
super(props);
this.state = {
number: 0
};
}
handleClick(e){
this.setState({number: this.state.number + 1});
}
render(){
return (
<div className='container'>
<CSSTransitionGroup transitionName="example" transitionAppear={true} transitionAppearTimeout={500} transitionEnterTimeout={500} transitionLeaveTimeout={300}>
<div className="number" key={this.state.number}>{this.state.number}</div>
</CSSTransitionGroup>
<button onClick={this.handleClick.bind(this)}>Click Me!</button>
</div>
)
}
}
React.render(<Container />, document.getElementById('container'));
Css
.example-enter {
opacity: 0.01;
}
.example-enter.example-enter-active {
opacity: 1;
transition: opacity 500ms ease-in;
}
.example-leave {
opacity: 1;
}
.example-leave.example-leave-active {
opacity: 0.01;
transition: opacity 300ms ease-in;
}
.example-appear {
opacity: 0.01;
}
.example-appear.example-appear-active {
opacity: 1;
transition: opacity .5s ease-in;
}
Here is fiddle.