簡體   English   中英

React JS — TransitionGroup和高階組件

[英]React JS — TransitionGroup and Higher-order Components

我正在嘗試使用TransitionGroupTweenMax為自定義的Modal組件設置動畫。 要了解模態的結構,請考慮以下假設的偽結構:

<Container>
   <Content>
     // modal contents in here...
   </Content>
   <Overlay/>
</Container>

我希望<Container>淡入,而<Content>從底部淡入。 這是可以幫助您理解的視覺效果; 紅色代表疊加層,白色代表內部內容: 在此處輸入圖片說明

我實際上使用以下代碼使上述視覺效果起作用:

import React, { Component } from 'react';
import TransitionGroup from 'react-transition-group/TransitionGroup'; // npm install react-transition-group --save
import { TweenMax } from 'gsap'; // npm install gsap --save

class ParentChild extends React.Component {

    componentWillEnter(callback) {
        TweenMax.fromTo(this.parent, 0.33, {opacity: 0}, {opacity: 1, onComplete: callback});
        TweenMax.fromTo(this.child, 0.33, {opacity: 0, y: 100}, {opacity: 1, y: 0, onComplete: callback});
    }

    componentWillLeave(callback) {
        TweenMax.fromTo(this.parent, 0.33, {opacity: 1}, {opacity: 0, onComplete: callback});
        TweenMax.fromTo(this.child, 0.33, {opacity: 1, y: 0}, {opacity: 0, y: 100, onComplete: callback});
    }

    render() {
        const { size, children } = this.props;
        const parentStyle = {
            width: `${size}px`,
            height: `${size}px`,
            background: '#df4747',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center'
        };
        const childStyle = {
            background: '#fff',
            textAlign: 'center',
            color: '#000',
            width: '90%'
        }
        return(
            <div ref={el => this.parent = el} style={parentStyle}>
                <div ref={el => this.child = el} style={childStyle}>
                    {this.props.children}
                </div>
            </div>
        );
    }
}

class App extends Component {
    constructor() {
        super();
        this.state = {
            isVisible: false
        };
        this.handleToggle = this.handleToggle.bind(this);
        this.handleClose = this.handleClose.bind(this);
    }

    handleToggle() {
        this.setState({ isVisible: !this.state.isVisible });
    }

    handleClose() {
        this.setState({ isVisible: false });
    }

    render() {
        const { isVisible } = this.state;
        return(
            <div>
                <TransitionGroup>
                    {isVisible ?
                        <ParentChild size="150">
                            <p>I wanna fade up!</p>
                        </ParentChild>
                    : null}
                </TransitionGroup>

                <button onClick={this.handleToggle}>Toggle Visibility</button>
            </div>
        );
    }
}

export default App;

我想借助高階組件使上述代碼更易於維護,因此我嘗試了以下操作。 在我看來,以下代碼更具可維護性,因為TweenMax內容被隔離為單個組件(fadeInHOC和fadeInUpHOC),因此,它可以在任何類型的React組件上重用。 另外,如果我想更改動畫,我所要做的就是創建另一個HOC並更改包裝函數。

import React, { Component } from 'react';
import TransitionGroup from 'react-transition-group/TransitionGroup'; // npm install react-transition-group --save
import { TweenMax } from 'gsap'; // npm install gsap --save

const fadeInHOC = (Component) => {
    return class extends React.Component {

        componentWillEnter(callback) {
            TweenMax.fromTo(this.container, 0.33, {opacity: 0}, {opacity: 1, onComplete: callback});
        }

        componentWillLeave(callback) {
            TweenMax.fromTo(this.container, 0.33, {opacity: 1}, {opacity: 0, onComplete: callback});
        }

        render() {
            return <Component containerRef={el => this.container = el} {...this.props}/>;
        }
    }
}

const fadeInUpHOC = (Component) => {
    return class extends React.Component {

        componentWillEnter(callback) {
            TweenMax.fromTo(this.container, 0.33, {opacity: 0, y: 100}, {opacity: 1, y: 0, onComplete: callback});
        }

        componentWillLeave(callback) {
            TweenMax.fromTo(this.container, 0.33, {opacity: 1, y: 0}, {opacity: 0, y: 100, onComplete: callback});
        }

        render() {
            return <Component containerRef={el => this.container = el} {...this.props}/>;
        }
    }
}

const Parent = fadeInHOC((props) => {
    const size = props.size;
    const style = {
        width: `${size}px`,
        height: `${size}px`,
        background: '#df4747',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center'
    }
    return(
        <div ref={props.containerRef} style={style}>
            {props.children}
        </div>
    );
});

const Child = fadeInUpHOC((props) => {
    const style = {
        background: '#fff',
        textAlign: 'center',
        color: '#000',
        width: '90%'
    }
    return(
        <div ref={props.containerRef} style={style}>
            {props.children}
        </div>
    );
});

class App extends Component {
    constructor() {
        super();
        this.state = {
            isVisible: false
        };
        this.handleToggle = this.handleToggle.bind(this);
        this.handleClose = this.handleClose.bind(this);
    }

    handleToggle() {
        this.setState({ isVisible: !this.state.isVisible });
    }

    handleClose() {
        this.setState({ isVisible: false });
    }

    render() {
        const { isVisible } = this.state;
        return(
            <div>
                <TransitionGroup>
                    {isVisible ?
                        <Parent size="150">
                            <Child>
                                <p>I wanna fade up!</p>
                            </Child>
                        </Parent>
                    : null}
                </TransitionGroup>

                <button onClick={this.handleToggle}>Toggle Visibility</button>
            </div>
        );
    }
}

export default App;

上面的代碼的問題是<Child>上的fadeInUp沒有被觸發,只有<Parent>上的外部動畫起作用。 您能告訴我如何使它按預期工作嗎? 我應該使用其他方法嗎? 我應該堅持使用ParentChild方法而不使用高階組件嗎? 非常感謝您的投入。 謝謝!

如果將ParentChild進一步包裝在HOC組件中,則過渡組將無法工作,我們還通過將TransitionGroupParentChild組件掛鈎而遇到了同樣的問題,根本不會觸發生命周期方法。

一種方法是先將您的Child組件包裝在TransitionGroup例如:

const WrappedChild = (props) =>{
  return <TransitionGroup>
  <Child {...props}/>
  </TransitionGroup>
}

然后導出WrappedChild並將其作為Parent的子級,所有生命周期方法將在ur Child組件中可用。 這將在您的情況下正常工作。

但是,如果您的Parent包含多個子級,例如通過data.map((item)=> <Child ... />)渲染,並且Child本身是一個連接的redux組件(我們的情況),則此方法將無效。

另一種方法是使用react-movehttps://github.com/react-tools/react-move )而不是transitionGroup ,這是我們解決問題的方法。 它非常方便,您可以在一個過渡中自定義持續時間,緩和曲線,以不同的樣式定義不同的動畫。

然后,您不需要TweenMax來處理動畫。 希望它將對您有所幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM