简体   繁体   English

React JS — TransitionGroup和高阶组件

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

I'm trying to animate a custom Modal component using TransitionGroup and TweenMax . 我正在尝试使用TransitionGroupTweenMax为自定义的Modal组件设置动画。 To understand the structure of the Modal, please consider the following hypothetical, psuedo structure: 要了解模态的结构,请考虑以下假设的伪结构:

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

I want <Container> to fade in and <Content> to fade in from the bottom. 我希望<Container>淡入,而<Content>从底部淡入。 Here is a visual to help you understand; 这是可以帮助您理解的视觉效果; the red represents the overlay and the white represents the inner contents: 红色代表叠加层,白色代表内部内容: 在此处输入图片说明

I actually got the above visual to work using the following code: 我实际上使用以下代码使上述视觉效果起作用:

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;

I want to make the above code more maintainable with the help of higher-order components, so I attempted the following. 我想借助高阶组件使上述代码更易于维护,因此我尝试了以下操作。 In my opinion, the following code is more maintainable because the TweenMax stuff is isolated into individual components (fadeInHOC and fadeInUpHOC), therefore, it can be reused on any type of React component. 在我看来,以下代码更具可维护性,因为TweenMax内容被隔离为单个组件(fadeInHOC和fadeInUpHOC),因此,它可以在任何类型的React组件上重用。 Additionally, if I ever wanted to change the animations, all I would have to do is create another HOC and change the wrapper function. 另外,如果我想更改动画,我所要做的就是创建另一个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;

The problem with the above code is that fadeInUp on <Child> is not triggered, only the outer animation on <Parent> works. 上面的代码的问题是<Child>上的fadeInUp没有被触发,只有<Parent>上的外部动画起作用。 Can you tell me how I can get it to work as intended? 您能告诉我如何使它按预期工作吗? Should I use a different approach? 我应该使用其他方法吗? Should I stick with the ParentChild approach without using higher-order components? 我应该坚持使用ParentChild方法而不使用高阶组件吗? I really appreciate your input. 非常感谢您的投入。 Thank you! 谢谢!

if you further wrapped ParentChild in a HOC component, the transition group will not work, we also met the same problem by hooking TransitionGroup with react connected component, the life cycle methods will not be triggered at all. 如果将ParentChild进一步包装在HOC组件中,则过渡组将无法工作,我们还通过将TransitionGroupParentChild组件挂钩而遇到了同样的问题,根本不会触发生命周期方法。

One way to do it is wrap ur Child component first with TransitionGroup like: 一种方法是先将您的Child组件包装在TransitionGroup例如:

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

and then export WrappedChild and make it as a child of Parent , all the life cycle methods will be available, in ur Child component. 然后导出WrappedChild并将其作为Parent的子级,所有生命周期方法将在ur Child组件中可用。 This will work fine in ur cases. 这将在您的情况下正常工作。

However, It will not work if your Parent contains multiple children, say, rendering by data.map((item)=> <Child ... />) and Child itself is a connected redux component (our situation). 但是,如果您的Parent包含多个子级,例如通过data.map((item)=> <Child ... />)渲染,并且Child本身是一个连接的redux组件(我们的情况),则此方法将无效。

Another way to do it is to use react-move ( https://github.com/react-tools/react-move ) instead of transitionGroup , which is the solution for our problem. 另一种方法是使用react-movehttps://github.com/react-tools/react-move )而不是transitionGroup ,这是我们解决问题的方法。 It is very handy and you can custom the duration, ease curve, define different animation on different style in a single transition. 它非常方便,您可以在一个过渡中自定义持续时间,缓和曲线,以不同的样式定义不同的动画。

Then you do not need the TweenMax to handle the animation. 然后,您不需要TweenMax来处理动画。 Hopefully it will help you. 希望它将对您有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM