React - 动画安装和卸载单个组件

新手上路,请多包涵

这么简单的事情应该很容易完成,但我正在为它的复杂性而烦恼。

我要做的就是为 React 组件的安装和卸载设置动画,仅此而已。这是我迄今为止尝试过的以及为什么每个解决方案都不起作用的原因:

  1. ReactCSSTransitionGroup - 我根本没有使用 CSS 类,都是 JS 样式,所以这不起作用。
  2. ReactTransitionGroup - 这个较低级别的 API 很棒,但是它需要您在动画完成时使用回调,所以在这里仅使用 CSS 过渡是行不通的。总是有动画库,这就引出了下一点:
  3. GreenSock - 许可对于商业用途 IMO 来说过于严格。
  4. React Motion - 这看起来很棒,但是 TransitionMotion 对于我需要的东西来说非常混乱和过于复杂。
  5. 当然,我可以像 Material UI 那样做一些诡计,其中元素被渲染但保持隐藏( left: -10000px )但我宁愿不走那条路。我认为它很hacky,我 希望 我的组件卸载,以便它们清理并且不会弄乱DOM。

我想要一些 易于 实现的东西。在安装时,为一组样式设置动画;在卸载时,为相同(或另一组)样式设置动画。完毕。它还必须在多个平台上具有高性能。

我在这里撞到了一堵砖墙。如果我遗漏了什么并且有一种简单的方法可以做到这一点,请告诉我。

原文由 ffxsam 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.1k
2 个回答

这有点冗长,但我已经使用了所有本机事件和方法来实现这个动画。没有 ReactCSSTransitionGroup , ReactTransitionGroup 等等。

我用过的东西

  • 反应生命周期方法
  • onTransitionEnd 事件

这是如何工作的

  • 根据传递的安装道具( mounted )和默认样式( opacity: 0 )安装元素
  • 安装或更新后,使用 componentDidMountcomponentWillReceiveProps 进一步更新)更改样式( opacity: 1 )并超时(使其异步)。
  • 在卸载期间,将一个道具传递给组件以识别卸载,再次更改样式( opacity: 0 ), onTransitionEnd ,从 DOM 中删除卸载元素。

继续循环。

看一遍代码,你就明白了。如果需要任何澄清,请发表评论。

希望这可以帮助。

 class App extends React.Component{
  constructor(props) {
    super(props)
    this.transitionEnd = this.transitionEnd.bind(this)
    this.mountStyle = this.mountStyle.bind(this)
    this.unMountStyle = this.unMountStyle.bind(this)
    this.state ={ //base css
      show: true,
      style :{
        fontSize: 60,
        opacity: 0,
        transition: 'all 2s ease',
      }
    }
  }

  componentWillReceiveProps(newProps) { // check for the mounted props
    if(!newProps.mounted)
      return this.unMountStyle() // call outro animation when mounted prop is false
    this.setState({ // remount the node when the mounted prop is true
      show: true
    })
    setTimeout(this.mountStyle, 10) // call the into animation
  }

  unMountStyle() { // css for unmount animation
    this.setState({
      style: {
        fontSize: 60,
        opacity: 0,
        transition: 'all 1s ease',
      }
    })
  }

  mountStyle() { // css for mount animation
    this.setState({
      style: {
        fontSize: 60,
        opacity: 1,
        transition: 'all 1s ease',
      }
    })
  }

  componentDidMount(){
    setTimeout(this.mountStyle, 10) // call the into animation
  }

  transitionEnd(){
    if(!this.props.mounted){ // remove the node on transition end when the mounted prop is false
      this.setState({
        show: false
      })
    }
  }

  render() {
    return this.state.show && <h1 style={this.state.style} onTransitionEnd={this.transitionEnd}>Hello</h1>
  }
}

class Parent extends React.Component{
  constructor(props){
    super(props)
    this.buttonClick = this.buttonClick.bind(this)
    this.state = {
      showChild: true,
    }
  }
  buttonClick(){
    this.setState({
      showChild: !this.state.showChild
    })
  }
  render(){
    return <div>
        <App onTransitionEnd={this.transitionEnd} mounted={this.state.showChild}/>
        <button onClick={this.buttonClick}>{this.state.showChild ? 'Unmount': 'Mount'}</button>
      </div>
  }
}

ReactDOM.render(<Parent />, document.getElementById('app'))
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-with-addons.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

原文由 Pranesh Ravi 发布,翻译遵循 CC BY-SA 4.0 许可协议

这是我 基于这篇文章 使用新的钩子 API(带有 TypeScript)的解决方案,用于延迟组件的卸载阶段:

 function useDelayUnmount(isMounted: boolean, delayTime: number) {
    const [ shouldRender, setShouldRender ] = useState(false);

    useEffect(() => {
        let timeoutId: number;
        if (isMounted && !shouldRender) {
            setShouldRender(true);
        }
        else if(!isMounted && shouldRender) {
            timeoutId = setTimeout(
                () => setShouldRender(false),
                delayTime
            );
        }
        return () => clearTimeout(timeoutId);
    }, [isMounted, delayTime, shouldRender]);
    return shouldRender;
}

用法:

 const Parent: React.FC = () => {
    const [ isMounted, setIsMounted ] = useState(true);
    const shouldRenderChild = useDelayUnmount(isMounted, 500);
    const mountedStyle = {opacity: 1, transition: "opacity 500ms ease-in"};
    const unmountedStyle = {opacity: 0, transition: "opacity 500ms ease-in"};

    const handleToggleClicked = () => {
        setIsMounted(!isMounted);
    }

    return (
        <>
            {shouldRenderChild &&
                <Child style={isMounted ? mountedStyle : unmountedStyle} />}
            <button onClick={handleToggleClicked}>Click me!</button>
        </>
    );
}

代码沙盒 链接。

原文由 deckele 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
logo
Stack Overflow 翻译
子站问答
访问
宣传栏