2015-10-29 6 views
11

Ich möchte zwischen zwei Komponenten animieren, bei denen die erste Komponente ausgeblendet wird und aus dem DOM entfernt wird, bevor die nächste Komponente zum DOM hinzugefügt und eingeblendet wird. Andernfalls wird die neue Komponente hinzugefügt das DOM und belegt Speicherplatz, bevor die alte Komponente entfernt wird. Sie können das Problem in dieser Geige sehen:Reagiere den Übergang zwischen den Komponenten

http://jsfiddle.net/phepyezx/4

// css snippet 
.switch-enter { 
    opacity: 0.01; 
} 
.switch-enter.switch-enter-active { 
    opacity: 1.0; 
} 
.switch-leave { 
    opacity: 1.0; 
} 
.switch-leave.switch-leave-active { 
    opacity: 0; 
} 

// React snippet 
<ReactCSSTransitionGroup transitionName="switch"> 
    <div key={key} className={className}>{this.text()}</div> 
</ReactCSSTransitionGroup> 

eine inakzeptable Lösung (für mich) ist die ursprüngliche Komponente mit CSS zu verstecken, bevor auf die neue Komponente Übergang wie hier zu sehen:

http://jsfiddle.net/phepyezx/5

// Change to css 
.switch-leave { 
    visibility: hidden; 
    height: 0px; 
    width: 0px; 
    opacity: 1.0; 
} 

gibt es eine Möglichkeit, „Verzögerung“ reagieren von einer neuen Komponente vor dem Original entfernt Montage? Ich bin offen für Geschwindigkeit oder eine andere Bibliothek, um dies zu erreichen.

Dank

Antwort

8

Gelöst mit der componentWillUnmount() Lifecycle-Methode.

http://jsfiddle.net/phepyezx/9/

Hier ist der Code:

var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; 

const Off = React.createClass({ 
    componentWillUnmount() { 
     this.props.handleTransitionEnd(); 
    }, 
    render() { 
     return (
      <div className="off button">OFF</div> 
     ) 
    } 
}); 

const On = React.createClass({ 
    componentWillUnmount() { 
     this.props.handleTransitionEnd(); 
    }, 
    render() { 
     return (
      <div className="on button">ON</div> 
     ) 
    } 
}); 

var Switch = React.createClass({ 
    getInitialState: function() { 
     return { 
      on: false, 
      transitionEnd: true 
     }; 
    }, 

    toggle: function(e) { 
     this.setState({ 
      on: !this.state.on, 
      transitionEnd: false 
     }); 
    }, 

    handleTransitionEnd() { 
     this.setState({transitionEnd: true}); 
    }, 

    renderOff() { 
     if (! this.state.on && this.state.transitionEnd) { 
      return (
       <Off key="off" handleTransitionEnd={this.handleTransitionEnd} /> 
      ) 
     } 
    }, 

    renderOn() { 
     if (this.state.on && this.state.transitionEnd) { 
      return (
       <On key="on" handleTransitionEnd={this.handleTransitionEnd} /> 
      ) 
     } 
    }, 

    render: function() { 
     return (
      <div> 
       <button onClick={this.toggle}>Toggle</button> 
       <ReactCSSTransitionGroup transitionName="switch"> 
       {this.renderOff()} 
       {this.renderOn()} 
       </ReactCSSTransitionGroup> 
      </div> 
     );   
    } 
}); 

React.render(<Switch/>, document.getElementById("switch")); 

Und die entsprechende CSS:

.switch-enter { 
    opacity: 0.01; 
} 
.switch-enter.switch-enter-active { 
    opacity: 1.0; 
    transition: opacity 500ms ease-in; 
} 
.switch-leave { 
    opacity: 1.0; 
} 
.switch-leave.switch-leave-active { 
    opacity: 0; 
    transition: opacity 500ms ease-out; 
} 

Sie können die gleiche effektive Ergebnis mit Jonny Buchanan's answer erreichen, die anstelle der absoluten Positionierung und eine Verzögerung verwendet von componentWillUnmount()

+0

@JonnyBuchanan Beide Ansätze scheinen nützlich zu sein. Nun, da einige Zeit vergangen ist, welche Vorgehensweise hat sich für Sie am besten bewährt? @RickJolly, funktioniert dein Ansatz am besten für bestimmte Szenarien? Skaliert es sich in ReactCSSTransitionGroup auf größere Listen (Kinder)? Die Art, wie ich es verstehe, ist, dass Ihr Ansatz einen zusätzlichen 'render()' -Zyklus eingeht (vielleicht ein zusätzlicher Zyklus für jeden entfernten Gegenstand?). Haben Sie das gefunden, um Vorteile oder Nachteile zu haben? (Vielleicht Debuggen - vielleicht ist es klarer, was ist mit dem zusätzlichen 'render()'?) –

+0

Auch, was ist mit der Tatsache, dass die zu animierenden Komponenten 'componentWillUnmount' implementieren müssen, sowie die' handleTransitionEnd() ' als "Stütze" ... hast du das als Schmerz für andere Situationen empfunden? –

13

Eine andere Lösung ist die ein- und ausgehenden Elemente nehmen den gleichen Raum zu machen, beispielsweise indem sie beide absolut positioniert ist:

<ReactCSSTransitionGroup 
    className="container" 
    component="div" 
    transitionName="switch"> 
... 

.container { 
    position: relative; 
} 
.container > div { 
    position: absolute; 
} 

http://jsfiddle.net/phepyezx/7/


Sie kann transition-delay verwenden, um zu warten, bis die austretende Komponente verschwindet, bevor die eintretende Komponente erscheint, zB:

.fade-enter { 
    opacity: 0.01; 
} 
.fade-enter.fade-enter-active { 
    opacity: 1; 
    transition: opacity 1s; 
    transition-delay: 1s; 
} 

.fade-leave { 
    opacity: 1; 
} 
.fade-leave.fade-leave-active { 
    opacity: 0.01; 
    transition: opacity 1s; 
} 
+0

Ja. Nicht ganz der gleiche Effekt wie das Original vollständig auszublenden. Ich denke, was ich will, ist ohne Tricks nicht möglich. Ich nehme an, ich könnte die componentDidLeave() lifecycle Methode der unteren Ebene ReactTransisionGroup verwenden, um den Status festzulegen, um zu wissen, wann mit dem Rendern der neuen Komponente zu beginnen. Ein anderer Ansatz besteht darin, plain javascript zu verwenden, um einen Transitionend-Ereignis-Listener auf den zugrunde liegenden DOM-Knoten anzuwenden, wie in diesem Beitrag gezeigt: http://www.chloechen.io/react-animation-done-in-twow-ads/ –

+0

Ein neues hinzugefügt etwas über "Übergangsverzögerung" –

+0

Vielen Dank! Fast perfekt, erfordert aber absolute Positionierung.Leider habe ich nichts gefunden, wo die ankommende Komponente nicht gemountet ist (oder "none" anzeigen), bis die ausgehende Komponente weg ist. –

2
import React, { Component } from 'react'; 

export default class DelayedRender extends Component { 

    static propTypes = { 
     delay: React.PropTypes.number.isRequired, 
     children: React.PropTypes.element, 
     className: React.PropTypes.string 
    }; 

    constructor(props) { 
     super(props); 

     this.state = { 
      render: false 
     }; 
    } 

    componentDidMount() { 
     setTimeout(() => { 
      const delayedClassNames = this.refs.noDelayed.className; 
      this.setState({ 
       render: true, 
       classNames: delayedClassNames 
      }); 
     }, this.props.delay); 
    } 

    render() { 
     const { children, className } = this.props; 
     return this.state.render ? 
      <div className={this.state.classNames}>{children}</div> : 
      <div className={className} ref="noDelayed" ></div>; 
    } 
} 

Und in Ihrem Render-Methode:Wenn Sie die Wiedergabe der nächsten Komponente verzögern wollen, könnten Sie so etwas wie dieses verwenden

const ROUTE_TRANSITION_TIME = 500; 
const views = []; 

if (shouldRenderDelayedRoute) { 
    views.push(
     <DelayedRender delay={ROUTE_TRANSITION_TIME} key="book"> 
      <A ref="book"/> 
     </DelayedRender> 
    ); 
} else { 
    views.push(<B key="library"/>); 
} 

<ReactCSSTransitionGroup 
    transitionEnterTimeout={ROUTE_TRANSITION_TIME} 
    transitionLeaveTimeout={ROUTE_TRANSITION_TIME} 
    transitionName="fade-transition" 
         > 
    {views} 
</ReactCSSTransitionGroup>