[英]React JS — TransitionGroup and Higher-order Components
我正在嘗試使用TransitionGroup和TweenMax為自定義的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組件中,則過渡組將無法工作,我們還通過將TransitionGroup
與ParentChild
組件掛鈎而遇到了同樣的問題,根本不會觸發生命周期方法。
一種方法是先將您的Child
組件包裝在TransitionGroup
例如:
const WrappedChild = (props) =>{
return <TransitionGroup>
<Child {...props}/>
</TransitionGroup>
}
然后導出WrappedChild
並將其作為Parent
的子級,所有生命周期方法將在ur Child
組件中可用。 這將在您的情況下正常工作。
但是,如果您的Parent
包含多個子級,例如通過data.map((item)=> <Child ... />)
渲染,並且Child
本身是一個連接的redux組件(我們的情況),則此方法將無效。
另一種方法是使用react-move
( https://github.com/react-tools/react-move )而不是transitionGroup
,這是我們解決問題的方法。 它非常方便,您可以在一個過渡中自定義持續時間,緩和曲線,以不同的樣式定義不同的動畫。
然后,您不需要TweenMax
來處理動畫。 希望它將對您有所幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.