Reputation: 3889
I'm using styled-components instead of tradition way of css. But I don't know how it can work together with ReactCSSTransitionGroup.
Basically, ReactCSSTransitionGroup
looks for certain classnames in css resource, then apply to a component throughout its lifecycle. However, with styled-components
, there are not any class names, styles are applied to components directly.
I know I can choose not to use ReactCSSTransitionGroup
because the two technique doesn't look compatible. But when I use only styled-components
, seems I can't render any animation when a component is unmounted - it's pure css, can't access component's lifecycle.
Any help or recommendation is appreciated.
Upvotes: 35
Views: 25267
Reputation: 117
There is a great blog post explaining how to do this: https://dev.to/terrierscript/styled-component--react-transition-group--very-simple-transition-jja
They use a low level Transiton
component available from react-transition-group:
http://reactcommunity.org/react-transition-group/transition
// This is overly simplified, but styles change depend on state from Transition
const MyStyledComponent = styled.div`
transform: translateY(${({ state }) => (state === 'exited' ? "0" : "-100%")});
transition: transform 2s;
`
const App = () =>
<Transition in={animate} timeout={500}>
{(state) => (
// state change: exited -> entering -> entered -> exiting -> exited
<MyStyledComponent state={state}>Hello</MyStyledComponent>
)}
</Transition>
Upvotes: 1
Reputation: 7558
I didn't want to use injectGlobal
as suggested in another answer because I needed to make the transitions different per component.
It turns out to be pretty easy - just nest the transition classes in the styling for the component:
import React from "react";
import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup';
import styled from 'styled-components';
const appearDuration = 500;
const transitionName = `example`;
const Container = styled.section`
font-size: 1.5em;
padding: 0;
margin: 0;
&.${transitionName}-appear {
opacity: 0.01;
}
&.${transitionName}-appear-active {
opacity: 1;
transition: opacity ${appearDuration}ms ease-out;
}`;
export default () => {
return (
<CSSTransitionGroup
transitionName={transitionName}
transitionAppear={true}
transitionAppearTimeout={appearDuration}>
<Container>
This will have the appear transition applied!
</Container>
</CSSTransitionGroup>
);
};
Note that I'm using the newer CSSTransitionGroup
, rather than ReactCSSTransitionGroup
, but it should work for that too.
Upvotes: 52
Reputation: 1
import React from "react";
import { CSSTransition } from 'react-transition-group';
const styles = theme => ({
'fade-enter':{
opacity: 0,
},
'fade-enter-active':{
opacity: 1,
transition: "opacity 300ms"
},
'fade-exit':{
opacity: 1,
},
'fade-exit-active':{
opacity: 0,
transition: "opacity 300ms"
},
})
class myAnimatedComponent extends React.Component {
constructor(props){
super(props);
}
render(){
let {classes} = this.props;
return (
<CSSTransition
in={this.props.conditionVariable}
classNames={{
enter: classes['fade-enter'],
enterActive: classes['fade-enter-active'],
exit: classes['fade-exit'],
exitActive: classes['fade-exit-active'],
}}
timeout={300}
unmountOnExit>
<span>This will have the transition applied to it!</span>
</CSSTransition>
);
}
};
export default (styles)(myAnimatedComponent);
I had to use classes['fade-enter']
etc, because React changes the name of all classes in this component due to the fact that I used withStyles
. And because of that too, when I export the component, React inserts my classes into this component's props, that's why I also had to create a variable called classes to catch those classes.
Upvotes: -1
Reputation: 2648
Mike Goatly's approach is great, but I had to make small changes to make it work. I changed the <CSSTransition>
's props, and used a function as its child.
See below for an example of a component, which fades in/out based on a state change:
import React, { Component } from "react";
import ReactDOM from "react-dom";
import { CSSTransition } from "react-transition-group";
import styled from "styled-components";
const Box = styled.div`
width: 300px;
height: 300px;
background: red;
transition: opacity 0.3s;
// enter from
&.fade-enter {
opacity: 0;
}
// enter to
&.fade-enter-active {
opacity: 1;
}
// exit from
&.fade-exit {
opacity: 1;
}
// exit to
&.fade-exit-active {
opacity: 0;
}
}`;
export default class App extends Component {
constructor() {
super();
this.state = {
active: true
};
setInterval(() => this.setState({ active: !this.state.active }), 1000);
}
render() {
return (
<CSSTransition
in={this.state.active}
classNames="fade"
timeout={300}
unmountOnExit
>
{() => <Box />}
</CSSTransition>
);
}
}
Upvotes: 14
Reputation: 31
You can use css variable selector in styled-components. Like this:
const Animation = styled(ReactCSSTransitionGroup)`
${({ transitionName }) => `.${transitionName}-enter`} {
opacity: 0;
}
${({transitionName}) => `.${transitionName}-leave`} {
opacity: 1;
}
`
const animationID = 'some-hashed-text'
const AnimationComponent = props => (
<Animation
transitionName={animationID}
transitionEnterTimeout={0.1}
transitionLeaveTimeout={2000}
>
<div>some content</div>
</Animation>
)
Upvotes: 3
Reputation: 7501
Use the injectGlobal()
styled-component helper method where your React app is bootstrapped. With this method you can style any CSS selector as if you'd be using conventional CSS.
First create a JS file exporting a template literal with your CSS for the react-transition-group
(please not I'm using v2.1 new class names syntax):
globalCss.js
const globalCss = `
.transition-classes {
/* The double class name is to add more specifity */
/* so that this CSS has preference over the component one. */
/* Try removing it, you may not need it if properties don't collide */
/* https://www.styled-components.com/docs/advanced#issues-with-specificity */
&-enter&-enter {
}
&-enter&-enter-active {
}
&-exit&-exit {
}
&-exit&-exit-active {
}
}
`;
export default globalCss;
Then on your entry point file:
index.jsx
import { injectGlobal } from "styled-components";
import globalCss from "./globalCss.js";
injectGlobal`${ globalCss }`; // <-- This will do the trick
ReactDOM.render(
<Provider store={ Store } >
<HashRouter >
<Route path="/" component={ Component1 } />
<Route path="/" component={ Component2 } />
</HashRouter>
</Provider>,
document.getElementsByClassName("react-app")[0]
);
However, if you just use CSS/SASS/Less to write the classes for the react-trasition-group
even when you use styled-components, it also works well.
Upvotes: 2