2017-08-14 10 views
0

さまざまなUIコンポーネントを使用して様々な質問を画面にレンダリングする調査のようなReactアプリケーションがあります。React TransitionGroup、GSAP、およびMobXを使用して同じコンポーネントを再レンダリングする

しかし、調査の性質上、正確に同じコンポーネントを使用して多くの質問が再レンダリングされます。たとえば、「A/Bセレクタ」または「チェックリスト」。

私が達成したいのは、最初にDOMに再使用されるのか、最初にマウントされるのか、下からフェードアップするのかにかかわらず、各コンポーネントのためです。回答。この作品

import React, {Component, PropTypes} from 'react'; 
import ReactDOM from 'react-dom'; 

import { observer, Provider, inject } from 'mobx-react'; 
import { observable, computed, action } from 'mobx'; 

import 'gsap'; 
import TransitionGroup from 'react-addons-transition-group'; 

// STORE 
class APP_STORE { 

    @observable showingBox = true; 
    @observable transDuration = .25; 

    @observable questionIndex = 0; 

    @observable questions = [ 
    {text: 'one'}, 
    {text: 'two'}, 
    {text: 'three'}, 
    {text: 'four'}, 
    {text: 'five'}, 
    ]; 

    @computed get currentQuestion() { 
    return this.questions[this.questionIndex]; 
    } 

    @action testTrans() { 
    this.showingBox = !this.showingBox; 
    setTimeout(() => { 
     this.questionIndex++; 
     this.showingBox = !this.showingBox; 
    }, this.transDuration * 1000); 
    } 

} 

let appStore = new APP_STORE(); 


// TRANSITION w/HIGHER ORDER COMPONENT 
function fadesUpAndDown (Component) { 
    return (
    class FadesUp extends React.Component { 
     componentWillAppear (callback) { 
     const el = ReactDOM.findDOMNode(this); 
     TweenMax.fromTo(el, appStore.transDuration, {y: 100, opacity: 0}, {y: Math.random() * -100, opacity: 1, onComplete: callback}); 
     } 

     componentWillEnter (callback) { 
     const el = ReactDOM.findDOMNode(this); 
     TweenMax.fromTo(el, appStore.transDuration, {y: 100, opacity: 0}, {y: Math.random() * -100, opacity: 1, onComplete: callback}); 
     } 

     componentWillLeave (callback) { 
     const el = ReactDOM.findDOMNode(this); 
     TweenMax.to(el, appStore.transDuration, {y: 100, opacity: 0, onComplete: callback}); 
     } 

     render() { 
     return <Component ref="child" {...this.props} />; 
     } 
    } 
) 
} 

// REACT 

@fadesUpAndDown 
class Box extends React.Component { 
    render() { 
    return <div className="box" ref={c => this.container = c}> {this.props.data.text} </div>; 
    } 
} 


@inject('store') @observer 
class Page extends Component { 

    render() { 
    const store = this.props.store; 

    return (
     <div className="page"> 
     <TransitionGroup> 
      { store.showingBox ? <Box data={store.currentQuestion}/> : null } 
     </TransitionGroup> 

     <button className="toggle-btn" onClick={() => { store.testTrans(); } } > 
      test trans 
     </button> 
     </div> 
    ); 
    } 

} 

:ここ

は、5つの質問と小さな箱を使用して、非常に基本的な例です!しかし...をプルするには、DOMの(この場合はshowingBox観測可能)経由でボックスコンポーネントを手動で削除する必要があります。移行時間のタイムアウトを設定し、コンポーネントを完全に再マウントします。

最終的に私はこれがうまくいくと思いますが、SOコミュニティの誰かが、アンマウント/再マウントが非常に強力なReactではないので、同じようなシナリオに遭遇しましたか?

答えて

2

RODRIGOのオリジナルANSWER

あなたは<TransitionGroup>、各コンポーネントのと、そのタグの使用onEnteronExit<Transition>タグを使用して試みることができます。すなわち、親コンポーネント内のいくつかのコンポーネントのための1つのアプローチは次のとおり

<TransitionGroup className="col-12"> 
    <Transition 
    key={props.location.pathname} 
    timeout={500} 
    mountOnEnter={true} 
    unmountOnExit={true} 
    onEnter={node => { 
     TweenLite.to(node, 0.5, { 
     autoAlpha: 1, 
     y: Math.random() * -100 
     }); 
    }} 
    onExit={node => { 
     TweenLite.to(node, 0.5, { 
     position: "fixed", 
     autoAlpha: 1, 
     y: 0 
     }); 
    }} 
    /> 
</TransitionGroup>; 

ここでこのコードを使用した例だが、とは、ルータが反応します。明らかにあなたが何をしているのかは分かりませんが、このアプローチを使用している実例です。 routes.jsファイルに、コンポーネントのフォルダに移動:

https://codesandbox.io/s/mQy3mMznn

唯一の注意点は、遷移グループの設定に設定期間は、マウント/アンマウントを維持するためにGSAPインスタンスの同じでなければならないということです同期すると、onEnteronExitはコールバックを提供しません。この方法は、あなたが角のようにonCompleteハンドラに渡すことができますdoneコールバックを提供し、この場合

<Transition 
    in={this.props.in} 
    timeout={duration} 
    mountOnEnter={true} 
    unmountOnExit={true} 
    addEndListener={(n, done) => { 
    if (this.props.in) { 
     TweenLite.to(n, 1, { 
     autoAlpha: 1, 
     x: 0, 
     ease: Back.easeOut, 
     onComplete: done 
     }); 
    } else { 
     TweenLite.to(n, 1, { autoAlpha: 0, x: -100, onComplete: done }); 
    } 
    }} 
> 
    {state => 
    <div className="card" style={{ marginTop: "10px", ...defaultStyle }}> 
     <div className="card-block"> 
     <h1 className="text-center">FADE IN/OUT COMPONENT</h1> 
     </div> 
    </div>} 
</Transition> 


別のオプションは、<Transition>要素のaddEndListenerメソッドを使用しています。そのことを念頭において、唯一の注意点は、トランジショングループconfigの期間がGSAPインスタンスの時間よりも長くなければならないことです。そうでない場合、コンポーネントはアニメーションが完了する前にアンマウントされます。それよりも長い場合は、完了したコールバックがアンマウントします。ここで

は、コンポーネントフォルダに移動し、 children.jsファイルに、ライブのサンプルです:

https://codesandbox.io/s/yvYE9NNW


ZFALEN'S派生SOLUTION

ロドリゴの第二の提案から<Transition />コンポーネントを活用react-transition-group(これはではありません。react-addons-transition-groupと同じです)は結局私をかなり理想的なソリューションに導きます。

MobXストア内で静的な値を使用することで、1つのアニメーション期間を宣言して他の場所に派生させることができます。さらに、<Transition />を高次のコンポーネント関数としてラップすると、デコレータを使用して、与えられたコンポーネントにどのアニメーションを持たせるべきかを指定できます。

アニメーションをMobX @inject()/<Provider />パターンと組み合わせている限り、基本的にはGSAPトランジションを個別に宣言し、必要に応じて関連するコンポーネントにタグを付け、ストアからすべてを制御できます。

:ここ

は、生のコードサンプル(。あなたはノードがなど、デコレータをサポートWebPACKの/バベルの設定でスピンアップし、またものが見えるように少しスタイリングを行う持っている必要があることに注意してください)です

import React, {Component, PropTypes} from 'react'; 
import ReactDOM from 'react-dom'; 

import { observer, Provider, inject } from 'mobx-react'; 
import { observable, computed, action } from 'mobx'; 

require('../../../public/stylesheets/transPage.scss'); 

import 'gsap'; 
import Transition from "react-transition-group/Transition"; 

// LIL UTILITY FUNCTIONS 
const TC = require('../../utils/timeConverter'); 

// MOBX STORE 
class APP_STORE { 

    // A TOGGLE & DURATION FOR THE TRANSITION 
    @observable showingBox = true; 
    transDuration = .25; 

    @observable questionIndex = 0; 

    @observable questions = [ 
    {text: 0 }, 
    ]; 

    @computed get currentQuestion() { 
    return this.questions[this.questionIndex]; 
    } 

    @action testTrans() { 

    // TOGGLE THE COMPONENT TO TRANSITION OUT 
    this.showingBox = !this.showingBox; 

    // WAIT UNTIL THE TRANSITION OUT COMPLETES 
    // THEN MAKE CHANGES THAT AFFECT STATE/PROPS 
    // THEN TRANSITION THE COMPONENT BACK IN 
    setTimeout(() => { 

     // IN THIS CASE, ADD A NEW 'QUESTION' TO THE SURVEY ARBITRARILY 
     this.questions.push({text: this.questionIndex + 1 }); 
     this.questionIndex++; 

     this.showingBox = !this.showingBox; 
    }, TC.toMilliseconds(this.transDuration)); 
    } 

} 

let appStore = new APP_STORE(); 


// TRANSITION w/HIGHER ORDER COMPONENT 
function fadesUpAndDown (Component) { 
    return (
    class FadesUp extends React.Component { 

     constructor(props) { 
     super(props); 
     } 

     render() { 
     const store = this.props.store; 

     return (
      <Transition 
      in={store.showingBox} 
      timeout={TC.toMilliseconds(store.transDuration)} 
      mountOnEnter={true} 
      unmountOnExit={true} 
      addEndListener={(n, done) => { 
       if (store.showingBox) { 
       TweenLite.to(n, store.transDuration, { 
        opacity: 1, 
        y: -25, 
        ease: Back.easeOut, 
        onComplete: done 
       }); 
       } else { 
       TweenLite.to(n, store.transDuration, { opacity: 0, y: 100, onComplete: done }); 
       } 
      }} 
      > 
      { state => <Component {...this.props} /> } 
      </Transition> 
     ) 
     } 
    } 
) 
} 

// REACT STUFF 

// JUST TAG COMPONENTS WITH THEIR RELEVANT TRANSITION 
@inject("store") @observer @fadesUpAndDown 
class Box extends React.Component { 

    constructor(props) { 
    super(props); 
    } 

    render() { 
    return <div className="box" > {this.props.data.text} </div> 
    } 
} 


@inject('store') @observer 
class Page extends Component { 

    render() { 
    const store = this.props.store; 

    return (
     <div className="page"> 
     {/* YOU DONT NEED TO EVEN REFERENCE THE TRANSITION HERE */} 
     {/* IT JUST WORKS BASED ON DERIVED MOBX VALUES */} 
     <Box data={store.currentQuestion} /> 

     <button className="toggle-btn" onClick={() => { store.testTrans(); } } > 
      test transition 
     </button> 
     </div> 
    ); 
    } 

} 


ReactDOM.render(
    <Provider store={appStore}> 
    <Page /> 
    </Provider>, 
    document.getElementById('renderDiv') 
); 

if (module.hot) { 
    module.hot.accept(() => { 
    ReactDOM.render(
     <Provider store={appStore}> 
     <Page /> 
     </Provider>, 
     document.getElementById('renderDiv') 
    ) 
    }) 
} 
+0

ありがとう、@rodrigo - 2番目の例は、私が探しているものに近いです。実際、上のレベルの 'isShowing'ステートを使用して移行コンポーネントがちょうど新しい小道具を受け取っているかどうかにかかわらず、マウント/アンマウントを強制するために、私は非常に似ています。私はそれを試し、あなたに戻ってきます – Zfalen

+1

あなたは男です。 2番目のパターンがうまくいき、MobX/Decoratorsと実際にうまく網を張ることができました。あなたの答えに追加された私の編集を見てください。 – Zfalen

+0

喜んで助ける;)。また、ソリューションを追加していただきありがとうございます。 MobXはここで頻繁に出る質問ではありませんので、多くの人があなたのソリューションから恩恵を受けると確信しています。乾杯!!! – Rodrigo

関連する問題