2015-10-29 3 views
11

Я хотел бы анимировать между двумя компонентами, где первый компонент исчезает и удаляется из DOM, прежде чем следующий компонент будет добавлен в DOM и затухает. В противном случае новый компонент добавляется к DOM и занимает место перед удалением старого компонента. Вы можете увидеть проблему в этой скрипке:Активировать переход между компонентами

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> 

неприемлемое решение (для меня), чтобы скрыть оригинальный компонент с помощью CSS, прежде чем перейти к новому компоненту, как показано здесь:

http://jsfiddle.net/phepyezx/5

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

есть ли способ, чтобы «задержка» реакции от монтажа нового компонента, прежде чем оригинал удаляется? Для этого я открыт для скорости или какой-либо другой библиотеки.

Благодаря

ответ

8

Решено использовать метод жизненного цикла componentWillUnmount().

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

Вот код:

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")); 

И соответствующий 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; 
} 

Вы можете достичь того же эффективного результата с Jonny Buchanan's answer который использует абсолютное позиционирование и задержку вместо componentWillUnmount()

+0

@JonnyBuchanan Оба ваших подхода кажутся вам полезными. Теперь, когда прошло какое-то время, какой подход вы нашли лучше всего для вас? @RickJolly, ваш подход работает лучше всего для определенных сценариев? Достаточно ли он масштабируется до больших списков (детей) в ReactCSSTransitionGroup? Насколько я понимаю, ваш подход требует дополнительного цикла «render()» (может быть, дополнительный цикл для каждого удаляемого элемента?). Вы обнаружили, что иметь преимущества или недостатки? (Возможно, отладка - возможно, ее четкость в том, что происходит с дополнительным «render()»?) –

+0

Также, о том, что компоненты, которые должны быть анимированы, должны реализовывать 'componentWillUnmount', а также принимать' handleTransitionEnd() ' как «prop» ... вы обнаружили, что это боль для других ситуаций? –

13

Другим решением является сделать входящие и исходящие элементы занимают такое же место, например, при наличии их как абсолютно позиционирован:

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

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

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


You может использовать transition-delay, чтобы подождать, пока исчезающий компонент не исчезнет, ​​прежде чем сделать входящий компонент, например:

.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

Да. Не совсем такой же эффект, как и полное исчезновение оригинала. Я думаю, что я не могу без обмана. Я полагаю, что я мог бы использовать метод жизненного цикла componentDidLeave() нижнего уровня ReactTransisionGroup для установки состояния, чтобы знать, когда начинать визуализацию нового компонента. Другим подходом является использование простого javascript для применения прослушивателя событий перехода на базовом узле DOM, как показано в этом сообщении: http://www.chloechen.io/react-animation-done-in-two-ways/ –

+0

Добавлен новый бит о 'delay-delay' –

+0

Большое спасибо! Почти идеальный, но требует абсолютного позиционирования.К сожалению, я не нашел ничего, где входящий компонент не монтируется (или покажет: «нет»), пока не покинет исходящий компонент. –

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>; 
    } 
} 

И в вашем методе визуализации:

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> 
Смежные вопросы