Reputation: 690
What I want to achieve is to change opacity with transition animation to get a smooth effect of changing visible elements.
The component I build is Slider. While changing slide opacity of next slide is changing to 1 and to 0 for the current slide.
My first version of Slide component
export class Slide extends React.PureComponent {
render() {
let { img, backgroundColor, active, ...props } = this.props;
const Wrapper = styled.div`
background-image: url(${img});
background-color: ${backgroundColor};
background-size: cover;
background-position: 50% 50%;
opacity: ${active ? 1 : 0};
transition: opacity 1s;
`;
return (
<Wrapper/>
)
}
}
My first thought was to store current slide index in state. So my Slides container know which slide is current and change its active property to true. But this will effect with rerender this Slide and transition is not visible. First question: why?
I ended up with playing with react dom and I change manually opacity of the current element.
export default class Slider extends Component {
__slidesCount;
__current;
__refs = [];
constructor(props) {
super(props);
this.__current = 0;
this.__slidesCount = this.props.children.length
}
componentWillMount() {
// Save number of slides
}
nextSlide = () => {
let next = this.__current < this.__slidesCount - 1 ? this.__current + 1 : 0,
nextSlide = this.__refs[next],
currentSlide = this.__refs[this.__current];
ReactDOM.findDOMNode(nextSlide).style['opacity'] = 1;
ReactDOM.findDOMNode(currentSlide).style['opacity'] = 0;
this.__current = next;
setTimeout(this.nextSlide, 3000)
};
componentDidMount() {
// set timeout to run it after component mounting is finished
setTimeout(() => {
ReactDOM.findDOMNode(this.__refs[0]).style['opacity'] = 1;
}, 0);
// call function changing slide to next
setTimeout(this.nextSlide, 3000)
}
render() {
let { children, ...props } = this.props;
let slides = children.map((slide, index) => {
return React.cloneElement(slide, { ref: ref => this.__refs.push(ref) })
});
return (
<SlidesWrapper {...props}>
{slides}
</SlidesWrapper>
)
}
}
What do you think about this solution? Is any way to do it easier? Of course, I don't want to use jQuery and minimize usage of external css.
Upvotes: 1
Views: 2281
Reputation: 2451
EDIT: Turns out that the OP was using Styled Components, and he was creating a new component every time render()
is called because const Wrapper...
was located inside of the render()
method.
This results in the Wrapper
component re-mounting every single time the Slide
component is updated, and so any CSS transitions will not occur because it is a new instance of a component and hence a new HTML element altogether.
Here's a proof of concept that CSS transitions work even when the active
state is passed as a prop, and without external CSS to control the UI state: CodePen. I used the same example of changing opacity and toggling betwen active slides to show that it works.
If your Slide
s are re-rendering when your state changes, there must be something else that might be re-rendering this entire sequence. Perhaps your components are being removed and re-added again, or something else. You will need to debug this yourself, or provide more code so that we can help you.
Alternatively, as mentioned in the comments, you can make use of ReactCSSTransitionGroup if you find the need to.
Upvotes: 1