React JS - TransitionGroup和高阶组件
我正在尝试使用TransitionGroup和TweenMax动画自定义模态组件。要了解模态的结构,请考虑以下的假设,伪结构:React JS - TransitionGroup和高阶组件
<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;
与上面的代码的问题是fadeInUp
上<Child>
不会被触发,只有外层的动画作品<Parent>
。你能告诉我如何让它按预期工作吗?我应该使用不同的方法吗?我应该坚持使用ParentChild
方法而不使用更高阶的组件吗?我真的很感激你的意见。谢谢!
如果您在HOC组件中进一步包装了ParentChild
,转换组将不起作用,我们也通过将TransitionGroup
与反应连接的组件挂钩来满足相同的问题,生命周期方法根本不会被触发。
一种方式来做到这一点是包乌尔Child
组件先用TransitionGroup
像:
const WrappedChild = (props) =>{
return <TransitionGroup>
<Child {...props}/>
</TransitionGroup>
}
,然后导出WrappedChild
,并使其作为Parent
一个孩子,所有的生命周期方法将可在ur Child
组件。这在你的情况下可以正常工作。
但是,如果你的Parent
包含多个孩子,比如说,通过data.map((item)=> <Child ... />)
和Child
本身呈现为连终极版组件(我们的情况),这是行不通的。
另一种方法是使用react-move
(https://github.com/react-tools/react-move)而不是transitionGroup
,这是我们问题的解决方案。它非常方便,您可以自定义持续时间,缓动曲线,在一次转换中定义不同风格的不同动画。
然后你不需要TweenMax
来处理动画。希望它会帮助你。