React CSSTransitionGroup is not working (doesn't add classes) - reactjs

I am currently working on notification component in React. It is working except the transitions.. Somehow its not even adding class. I looked up some React animation examples and i do some research but i couldnt find anything useful. Especially article for React15. I didnt understand, this should work perfectly but its just showing and hiding text without any transitions.
import React, { Component } from 'react';
import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup';
import '../stylesheets/notification.less';
export default class Notifications extends Component {
render() {
return (
<CSSTransitionGroup transitionName="notifications" transitionEnterTimeout={300} transitionLeaveTimeout={300}>
<div className={this.props.type === 'error' ? 'notification-inner warning' : 'notification-inner success'}>
{this.props.type} {this.props.message}
</div>
</CSSTransitionGroup>
);
}
}
And CSS File...
.notifications {
background:#000;
}
.notifications-enter {
opacity: 0;
transform: translate(-250px,0);
transform: translate3d(-250px,0,0);
}
.notifications-enter.notifications-enter-active {
opacity: 1;
transition: opacity 1s ease;
transform: translate(0,0);
transform: translate3d(0,0,0);
transition-property: transform, opacity;
transition-duration: 300ms;
transition-timing-function: cubic-bezier(0.175, 0.665, 0.320, 1), linear;
}
.notifications-leave {
opacity: 1;
transform: translate(0,0,0);
transform: translate3d(0,0,0);
transition-property: transform, opacity;
transition-duration: 300ms;
transition-timing-function: cubic-bezier(0.175, 0.665, 0.320, 1), linear;
}
.notifications-leave.notifications-leave-active {
opacity: 0;
transform: translate(250px,0);
transform: translate3d(250px,0,0);
}

Make sure you have the key attribute set.
From the doc: https://facebook.github.io/react/docs/animation.html
Note:
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.

Related

How to add a smooth sliding transition effect to a react-bootstrap based Column component?

To summarize the problem, I am trying to work through a component that has 2 columns (react-bootstrap Column component). The left container being collapsible and the right always there. On a click of a button, I will toggle show/hide on the left column. It works fine but the behavior is rugged. Whereas, I want to add a transition effect to achieve a smoother slide behavior.
More info...I have a page where I want to display products and filter the products based on different properties such as cost, size, category etc. So, I use a container inside and got 2 columns inside it a row.
Problem: Transition not so smooth...
Something like this in the code below,
import React, { Component } from 'react';
import { ProductsViewFilter } from '../../Controls/ProductsViewFilter/ProductsViewFilter';
import { ProductsView } from '../../Controls/ProductsView/ProductsView';
import { Container, Row, Col, Collapse } from 'react-bootstrap';
import './ProductsViewContainer.css';
export class ProductsViewContainer extends Component {
constructor(props) {
super(props);
this.state = {
filterExpanded: false
}
}
render() {
return (
<Container className='products-view-filter-container'>
<Row>
<Collapse in={this.state.filterExpanded}>
<Col sm={2} className={this.state.filterExpanded ? 'products-view-filter products-view-filter-transition-slide-in' : 'products-view-filter products-view-filter-transition-slide-out'}>
<ProductsViewFilter></ProductsViewFilter>
</Col>
</Collapse>
<Col className='products-view-container-productsview'>
<div className='slider-icon-div' >
<img className='slider-icon' src='/images/icons/slider.svg'
onClick={(e) => { e.preventDefault(); this.setState({ filterExpanded: !this.state.filterExpanded }); }} />
</div>
<ProductsView></ProductsView>
</Col>
</Row>
</Container>);
}
}
.products-view-filter-container
{
max-width: 100% !important;
}
.products-view-filter
{
background-color: wheat;
transform: translateX(-150px);
transition: transform 400ms ease-in;
}
.products-view-filter-transition-enter
{
transform: scale(0.8);
opacity: 0;
}
.products-view-filter-transition-enter-active
{
opacity: 1;
transform: translateX(0);
transition: opacity 300ms, transform 300ms;
}
.products-view-filter-transition-exit
{
opacity: 1;
}
.products-view-filter-transition-exit-active
{
opacity: 0;
transform: scale(0.9);
transition: opacity 300ms, transform 300ms;
}
.products-view-filter-transition-slide-in {
transform: translateX(0);
}
.products-view-filter-transition-slide-out {
transform: translateX(-100%);
}
.products-view-container-productsview
{
background-color: lavender;
}
.slider-icon
{
height: 1.5rem;
}
.slider-icon:hover
{
cursor: pointer;
}
.slider-icon-div
{
margin-top: 15px;
text-align: left;
}
I would like to see the behavior as something like https://codepen.io/bjornholdt/pen/MpXmmL/.
I know this is easily achievable by using bunch of pure divs. But, my desire is to use bootstrap components as it responsive in mobile devices.
Any advice, suggestion as to how to achieve sticking to Col and Row components with a smooth transitions will be really helpful.
This might be too late, but in case anyone else has this issue...
I have not tested this myself, but try what the docs (https://react-bootstrap.github.io/utilities/transitions/) suggest.
...
Collapse#
Add a collapse toggle animation to an element or component.
Smooth animations
If you're noticing choppy animations, and the component that's being collapsed has non-zero margin or padding, try wrapping the contents of your inside a node with no margin or padding, like the in the example below. This will allow the height to be computed properly, so the animation can proceed smoothly.
...

CSSTransition from react-transition-group does not exit

Trying to get used CSSTransiction component. I need to show a panel which is animated by using css-classes. The first class defines the primary location, the second defines position when the panel is opened (top: 0).
Appearing is working, but exiting does not. onEntered fires, onExied does not.
There is lack of documentation for CSSTransition component, I tried different combinations, but without success. Any ideas?
CSS:
.getSecondLevelPanel{
position: fixed;
left: 450px;
top: -100%;
height: 100%;
width: 400px;
transition: all 250ms ease-out;
}
.getSecondLevelPanelOpened {
top: 0;
}
React component:
import * as React from 'react';
import { CSSTransition } from 'react-transition-group';
export interface Props {
isVisible: Boolean;
children: React.ReactChild;
onClose: () => void;
}
const SecondLevelPanel = (props: Props) => {
return (
<CSSTransition
timeout={{
enter: 0,
exit: 500
}}
in={props.isVisible}
appear={true}
enter={true}
exit={true}
classNames={{
appear: 'getSecondLevelPanel',
enterDone: 'getSecondLevelPanel getSecondLevelPanelOpened',
exit: 'getSecondLevelPanel'
}}
unmountOnExit={true}
onEntered={() => {
alert('entered');
}}
onExited={() => {
alert('exited');
}}
>
<div>
<a onClick={() => props.onClose()}>Close</a>
{props.children}
</div>
</CSSTransition>
);
};
export default SecondLevelPanel;
After searching for answers for sometime, I realised my problem had to do with my toggle function (onClick={() => props.onClose()} in your case).
It seems my toggle function was not working as it was supposed to. Since onEntered is triggered with "in{true}" and onExited is triggered with "in{false}", you have to make sure the boolean is being changed with each click on your anchor tag.
Looking at your code is seems your onClose function only returns a void function instead of a toggle. Try toggling between true and false and pass that into the in{//toggle function} param.
The problem is in your CSS and classNames props on you. The CSSTransition component applies the classes like so.
appear and enter classes are added as soon as the in prop is set to true.
appear-active and enter-active are added right after the appear and enter classes are set.
appear-done and enter-done are added after the duration set in the timeout passed.
the exit classes repeat Step 1-3 just after the in prop is set to false.
The reason your exit calls don't work is probably due to only setting one exit class and 0 set to your enter duration. Try setting your transition rule on the active classes to ensure that the transition is set for the whole duration of the transition and separate the moving parts from the static ones. Like so
.panel{
position: fixed;
left: 450px;
top: -100%;
height: 100%;
width: 400px;
}
.panel-appear,
.panel-enter {
top: -100%;
}
.panel-appear-active,
.panel-enter-active {
top: 0;
transition: all 250ms ease-out;
}
.panel-exit {
top: 0;
}
.panel-exit-active {
top: -100%
transition: all 250ms ease-out;
}
Take a look at this codepen, this is how I create a panel transitions.
Install:
npm install react-transition-group
Usage:
import { CSSTransition } from 'react-transition-group';
<CSSTransition
in={toShow} // boolean value passed via state/props to either mount or unmount this component
timeout={300}
classNames='my-element' // IMP!
unmountOnExit
>
<ComponentToBeAnimated />
</CSSTransition>
NOTE: Make sure to apply below styles using the class property in CSS:
.my-element-enter {
opacity: 0;
transform: scale(0.9);
}
.my-element-enter-active {
opacity: 1;
transform: translateX(0);
transition: opacity 300ms, transform 300ms;
}
.my-element-exit {
opacity: 1;
}
.my-element-exit-active {
opacity: 0;
transform: scale(0.9);
transition: opacity 300ms, transform 300ms;
}

React styled-components fade in/fade out

I am trying to build a React component to handle fading in and fading out. In the following code, if I pass out as a prop to the component, it is disaplayed as hidden before animating out. I'm trying to have it fade in by default, then fade out when I pass in the out prop. Anyone see a solution to this problem?
import React from 'react';
import styled, { keyframes } from 'styled-components';
const fadeIn = keyframes`
from {
transform: scale(.25);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
`;
const fadeOut = keyframes`
from {
transform: scale(1);
opacity: 0;
}
to {
transform: scale(.25);
opacity: 1;
}
`;
const Fade = styled.div`
${props => props.out ?
`display: none;`
: `display: inline-block;`
}
animation: ${props => props.out ? fadeOut : fadeIn} 1s linear infinite;
`;
function App() {
return (
<div>
<Fade><💅test></Fade>
</div>
);
}
export default App;
WebpackBin running example
The issue with your code is that you're setting the display property to none when props.out is true. That's why you're not seeing any animation, because before that can even start you've already hidden the component!
The way to do a fade out animation is to use the visibility property instead and transition that for the same amount of time as the animation takes. (see this old SO answer)
Something like this should solve your issues:
const Fade = styled.default.div`
display: inline-block;
visibility: ${props => props.out ? 'hidden' : 'visible'};
animation: ${props => props.out ? fadeOut : fadeIn} 1s linear;
transition: visibility 1s linear;
`;
const fadeIn = styled.keyframes`
from {
transform: scale(.25);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
`;
const fadeOut = styled.keyframes`
from {
transform: scale(1);
opacity: 1;
}
to {
transform: scale(.25);
opacity: 0;
}
`;
const Fade = styled.default.div`
display: inline-block;
visibility: ${props => props.out ? 'hidden' : 'visible'};
animation: ${props => props.out ? fadeOut : fadeIn} 1s linear;
transition: visibility 1s linear;
`;
class App extends React.Component {
constructor() {
super()
this.state = {
visible: true,
}
}
componentDidMount() {
setTimeout(() => {
this.setState({
visible: false,
})
}, 1000)
}
render() {
return (
<div>
<Fade out={!this.state.visible}><💅test></Fade>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>
<div id="root" />
Note: Your fadeOut animation also went from 0 to 1 opacity, instead of the other way around. I've fixed that in the snippet too.

Trying to use ReactCSSTransitionGroup for a Singleton

I'd like to move something bottom: +/- 100px.
So that when focused, it slides up. When unfocused, it slides back down.
I've created this React render() :
var component = (
<div key={"trayResponder_" + this.props.trayOpen} >
</div>
);
return(
<ReactCSSTransitionGroup transitionName="tray-responder">
{component}
</ReactCSSTransitionGroup>
);
And then I toggle it's key state, by updating this.props.trayOpen
And then my Less is like this :
.tray-responder-enter {
}
.tray-responder-enter-active {
.animation(slideUp 1s ease infinite )
}
.tray-responder-leave {
}
.tray-responder-leave-active {
.animation(slideDown 1s ease infinite )
}
.keyframes(slideUp;{
0% {transform: translateY(100%);}
50% {transform: translateY(-8%);}
65% {transform: translateY(4%);}
80% {transform: translateY(-4%);}
95% {transform: translateY(2%);}
100% {transform: translateY(0%);}
});
.keyframes(slideDown;{
0% {transform: translateY(-100%);}
50%{transform: translateY(8%);}
65%{transform: translateY(-4%);}
80%{transform: translateY(4%);}
95%{transform: translateY(-2%);}
100% {transform: translateY(0%);}
});
Which adopts from animation.less :
.keyframes(#name; #arguments) {
#-moz-keyframes #name { #arguments(); }
#-webkit-keyframes #name { #arguments(); }
#keyframes #name { #arguments(); }
}
.animation(#arguments) {
-webkit-animation: #arguments;
-moz-animation: #arguments;
animation: #arguments;
}
Unfortunately, this doesn't seem to work. Nothing occurs. No animation. Not really sure why.
Update
The trouble with ReactCSSTransitionGroup is that enter-active and leave-active occur simultaneously. Which doesn't seem to be a problem if you're toggling opacity. But if you're moving things up and down like I am, then you see both at the same time.
As seen here :
As much as I understand, CSS Transition is for enter and leave. You want animation on hover, which can probably be achieved by using :hover selector.
const TransitionDemo = React.createClass({
render : function () {
return(
<div className="demo">
<p>Some Text Here</p>
</div>
);
}
});
ReactDOM.render(<TransitionDemo trayOpen={1}/>, document.getElementById('root'));
//css
.demo {
height : 100px;
position : relative;
}
.demo p {
position : absolute;
bottom : 0px;
transition : all 1s ease;
}
.demo:hover > p{
bottom : 50px;
transition : all 1s ease;
}
See this pen : http://codepen.io/umgupta/pen/dNNLxL

How to add page transitions to React without using the router?

I tried to add page transitions to my app using ReactCSSTransitionGroup but it did not work. For some pages it worked but for some it did not. Many examples out there show how to do it with the React router. But since I use Meteor, I use a different router (FlowRouter).
Here's my render method :
render() {
return (
<div>
{this.props.content()}
</div>
);
}
Here's how I tried to add transitions :
<ReactCSSTransitionGroup
transitionName="pageTransition"
transitionEnterTimeout={500}
transitionLeaveTimeout={300}
transitionAppear={true}
transitionAppearTimeout={500}
>
{/* Content */}
{React.cloneElement(this.props.content(), {
key: uuid.v1(),
})}
</ReactCSSTransitionGroup>
The css :
//Page transition
.pageTransition-enter {
opacity: 0.01;
}
.pageTransition-enter.pageTransition-enter-active {
animation: fadeIn 1s ease-in;
}
.animation-leave {
opacity: 1;
}
.pageTransition-leave.pageTransition-leave-active {
animation: fadeIn 3s ease-in;
}
.pageTransition-appear {
opacity: 0.01;
}
.pageTransition-appear.pageTransition-appear-active {
animation: opacity 5s ease-in;
}
Any idea how to make this work?
I figured it out! Your CSS animations are trying to use fadeIn, but that's not a CSS property. You need to change it to opacity. Like so:
//Page transition
.pageTransition-enter {
opacity: 0.01;
}
.pageTransition-enter.pageTransition-enter-active {
animation: opacity 1s ease-in;
}
.animation-leave {
opacity: 1;
}
.pageTransition-leave.pageTransition-leave-active {
animation: opacity 3s ease-in;
}
.pageTransition-appear {
opacity: 0.01;
}
.pageTransition-appear.pageTransition-appear-active {
animation: opacity 5s ease-in;
}
try defining your inner component before return call:
render() {
const clonedElement = <div>{this.props.content()}</div>;
return (
<ReactCSSTransitionGroup transitionName="pageTransition" transitionEnterTimeout={500} transitionLeaveTimeout={300} transitionAppear={true} transitionAppearTimeout={500}>
{clonedElement}
</ReactCSSTransitionGroup>
);
}

Resources