Reputation: 53763
ReactCSSTransitionGroup
uses a CSS file to define and store transitions. That is nice and works well. But what if a component needs a variable transition configuration e.g a variable transition-duration
that takes any value from 0ms to 10000ms? How would I change the value of transition-duration
from within the component's Javascript class, when it is stored in a CSS file?
Example:
Let's say you want to make a slideshow component and you want to implement an option to change the fading-duration (the time the animation takes to fade from one slide to another). You call the component like this:
<Slideshow fadeDuration={1000}>
This is how the component adds the transitions to the slide element that is going to be rendered:
render: function() {
return (
<div>
<ReactCSSTransitionGroup
transitionName="example"
transitionEnterTimeout={500}
transitionLeaveTimeout={300}>
{this.slide}
</ReactCSSTransitionGroup>
</div>
);
}
This is the css file needed by ReactCSSTransitionGroup
to define the transitions:
.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;
}
Now how do I use props.fadeDuration
inside the component to change the value of transition-duration
which lays inside the css file?
So far the only thing that came to my mind was, that I have to remove the ReactCSSTransitionGroup
and create the transitions manually using refs and the 'Style Object' 'transition' property. Is this the way to go? Or how would I do it?
Upvotes: 1
Views: 1478
Reputation: 12862
You can use jss library to manipulate css from javascript. It's a very good library and solves many cases. Take a look.
UPDATED
I'll show you how to use it. In major cases components using jss-react
look like:
import React, { Component } from 'react'
import useSheet from 'react-jss'
// You can use jss directly too!
import jss from 'jss'
import vendorPrefixer from 'jss-vendor-prefixer'
jss.use(vendorPrefixer())
const styles = {
button: {
'background-color': 'yellow'
},
label: {
'font-weight': 'bold'
}
}
class Button extends Component {
render() {
const { classes } = this.props.sheet
return (
<div className={classes.button}>
<span className={classes.label}>
{this.props.children}
</span>
</div>
)
}
}
export default useSheet(Button, styles)
Styles are defined outside the component, and then a higher-order component is used to wrap it and inject the styles. In major cases it's ok, but since we need to add css rules from inside of the component we need to use another approach. We need to use jss as a child element instead of higher order component. That will permit us to add rules from the components with the help of addRules()
method.
import React, { Component } from 'react';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group'
import useSheet from 'react-jss'
import jss from 'jss'
let styles = {};
const sheet = jss.createStyleSheet(styles, {named: false})
class Foo extends Component {
constructor(props) {
super(props);
this.state ={
isVisible: false
}
}
toggle() {
this.setState({
isVisible: !this.state.isVisible
})
}
render() {
const duration = 500
const { classes } = sheet
sheet.addRules(
{
'.example-enter': {
opacity: 0.01
},
'.example-enter-active': {
color: 'red',
opacity: 1,
transition: 'opacity ' + duration + 'ms ease-in'
},
'.example-leave': {
opacity: 1
},
'.example-leave-active': {
opacity: 0.01,
transition: 'opacity '+ duration +'ms ease-in'
}
}
);
return (
<div className={classes.red}>
<Jss sheet={sheet} />
<a onClick={this.toggle.bind(this)}>Click</a>
<ReactCSSTransitionGroup
transitionName="example"
transitionEnterTimeout={10}
transitionLeaveTimeout={600}>
{ this.state.isVisible ? <div>Visible</div> : null}
</ReactCSSTransitionGroup>
</div>
);
}
}
const map = new WeakMap()
class Jss extends Component {
componentWillMount() {
const {sheet} = this.props
const counter = map.get(sheet) || 0
if (!counter) sheet.attach()
map.set(sheet, counter + 1)
}
componentWillUnmount() {
const {sheet} = this.props
const counter = map.get(sheet) - 1
if (counter) {
map.set(sheet, counter)
} else {
sheet.detach()
map.delete(sheet)
}
}
render() {
return null
}
}
export default Foo
One thing to keep in mind. By default, jss
adds some suffixes to the classes in order to get rid of global selectors and isolate them. A class looks like: .example-enter-441163035
. It's very comfortable to use this approach if we set className
attributes manually, like: <div className={classes.button}>
. So, or you have to set transition classes manually, like this:
<ReactCSSTransitionGroup
transitionName={{
enter: classes.example-enter,
enterActive: classes.example-enter-active,
leave: classes.example-leave,
leaveActive: classes.example-leave-active.
}}
transitionEnterTimeout={10}
transitionLeaveTimeout={600}>
Or you have to use global selectors by setting named
option as false
when initiating sheet (like I did in my example):
const sheet = jss.createStyleSheet(styles, {named: false})
Hope it helps! Nevertheless, take a look at this library. It's really cool and promotes a very interesting approach to css.
Upvotes: 3