There are two buttons that toggle the layout of an item. When either button is clicked, I'd like the item to fade out, change, then fade back in.
Using ReactJS, I'm running into two problems:
Using componentDidUpdate
to trigger the "fade back in" event causes a loop; changing the state re-triggers componentDidUpdate
endlessly.
Using componentWillReceiveProps
allows me to update the class on the element to have it fade out, but it also immediately changes the layout. I need to delay the change until it's invisible.
Thoughts? Have I constructed this wrong?
(The code is below, but something is broken in Stack's version that works in JSFiddle: https://jsfiddle.net/nathanziarek/69z2wepo/15009/)
var Hello = React.createClass({
getInitialState: function() {
return { visible: " x-hidden" };
},
render: function() {
return <div className={"hello" + this.state.visible}>Hello {this.props.name}</div>;
},
componentDidMount: function(){
this.setState({ visible: "" })
},
componentWillReceiveProps: function(){
// The item is changing, make it invisible
// Wait until it's done being invisible before changing
// anything?
this.setState({ visible: " x-hidden" })
},
componentDidUpdate: function(){
// The item has changed, make it visible
// Setting anything here causes this function
// to get called again, creating a loop
// this.setState({ visible: "" })
}
});
var Button = React.createClass({
render: function() {
return <button onClick={this.handleClick}>{this.props.label}</button>;
},
handleClick: function() {
React.render(
<span>
<Button label="Universe"/>
<Button label="World"/>
<Hello name={this.props.label} />
</span>,
document.getElementById('container')
);
}
});
React.render(
<span>
<Button label="Universe"/>
<Button label="World"/>
<Hello name="_______" />
</span>,
document.getElementById('container')
);
.hello {
opacity: 1;
transition: 300ms opacity linear;
-webkit-transition: 300ms opacity linear;
}
.x-hidden { opacity: 0 }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.1/JSXTransformer.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.1/react-with-addons.js"></script>
<script src="https://facebook.github.io/react/js/jsfiddle-integration.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
I would look into using React's CSSTransitionGroup
add-on component. You'll have to do some restructuring, but this will give you the behavior you want without having to add a bunch of logic around setting CSS classes; React will take care of animating (in your case, fading) components that are entering/leaving the DOM.
Unrelated: I would also avoid re-rendering your entire component like how you're doing it in your button click event. This breaks the flow of React and will inevitably cause problems. Prefer changing state and pushing down new props.
Easiest way to do this is to have both on screen then to do the fade entirely with css, and essentially completely omit React from the process other than to place the appropriate classes.
If you're fading between them you need both sets of contents anyway. That, in turn, means there's no reason to not have these in the document, and that in turn means it's just CSS hide/show tomfoolery, and there's no need for any kind of React lifecycle stuff.
You're overcomplicating it by trying to have React handle way too much.
Supporting CSS:
#foo span {
opacity: 0;
position: absolute;
top: 0; left: 0; bottom: 0; right: 0;
transition: all 0.5s ease;
}
#foo span.show {
opacity: 1;
}
Then the relevant React:
var Foo = react.createComponent({
render: function() {
var showKey = this.props.showKey,
Things = {
a: 'Apple',
b: 'Bear',
c: 'Cold inevitable doom'
};
return (
<div id="foo">
{ Things.map(X,k) {
return <span class={(showKey === k)? 'show':undefined}>{X}</span>
}}
</div>
);
}
});
You'll find that as you switch that control's showKey, it will fade in and out its relevant contents as appropriate.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With