GluePear
GluePear

Reputation: 7715

React fade in element

I have a Basket component which needs to toggle a BasketContents component when clicked on. This works:

constructor() {
    super();
    this.state = {
      open: false
    }
    this.handleDropDown = this.handleDropDown.bind(this);
  }
  handleDropDown() {
    this.setState({ open: !this.state.open })
  }
    render() {
        return(
            <div className="basket">
                <button className="basketBtn" onClick={this.handleDropDown}>
                    Open
                </button>
              {
                this.state.open
                ?
                <BasketContents />
                : null
              }
            </div>
        )
    }

It uses a conditional to either display the BasketContents component or not. I now want it to fade in. I tried adding a ComponentDidMount hook to BasketContents to transition the opacity but that doesn't work. Is there a simple way to do this?

Upvotes: 21

Views: 65659

Answers (3)

Giladd
Giladd

Reputation: 1379

An example using css class toggling + opacity transitions:
https://jsfiddle.net/ybktodLc/

Here's the interesting CSS:

.basket {
  transition: opacity 0.5s;
  opacity: 1;
}
.basket.hide {
  opacity: 0;
  pointer-events:none;
}

And the render function:

render() {
    const classes = this.state.open ? 'basket' : 'basket hide'
    return(
      <div className="basket">
        <button className="basketBtn" onClick={this.handleDropDown}>
          {this.state.open ? 'Close' : 'Open'}
        </button>
        <BasketContents className={classes}/>
      </div>
    )
  }

Upvotes: 38

KTobiasson
KTobiasson

Reputation: 31

I was doing this for a mobile menu hamburger button for expanding and closing the nav. I wanted to keep rendering the contents but just want a smooth transition every time I opened/closed the menu. This is my solution. On compontentDidMount() and on every menu hamburger button click and close button click I set the opacity to 0 and wait for 1 millisecond in setTimeout before adding the transition:

    handleMenuExpand = () => {
        this.handleTransition(false);
    }
    handleMenuShrink = () => {
        this.handleTransition(true);
    }
    handleTransition = (isHidden) => {
        this.setState({
            transitionStyle: {
                opacity: '0'
            },
            isNavHidden: isHidden
        });
        setTimeout(() => this.setState({
                transitionStyle: {
                    transition: 'opacity 0.8s',
                    opacity: '1'
                }
            }), 1
        );
    }
    componentDidMount() {
        this.handleTransition(this._isMobile);
    }
    return(
        <nav className="navbar-container" style={this.state.transitionStyle}>
            { (this.state.isNavHidden) ?
                <ul className="navbar-content">
                    <li className="menu-expand-container" style={topBarStyle} >
                        <img
                            src={MenuHamburgerPic}
                            style={menuButtonStyle}
                            alt="Menu Pic"
                            onClick={this.handleMenuExpand}
                          />
                      </li>
                </ul>
             :
                <ul className="navbar-content">
                    {(this._isMobile) &&
                        <li style={closeButtonContainerStyle} >
                            <img
                                src={MenuClosePic}
                                style={closeMenuButtonStyle}
                                alt="Menu Pic"
                                onClick={this.handleMenuShrink}
                            />
                        </li>
                    }
                    <li>NAV ELEMENT 1</li>
                                    
                    <li>AOTHER NAV ELEMENT</li>
                </ul>
            }
        </nav>
    );

Upvotes: 0

Michal Cumpl
Michal Cumpl

Reputation: 1037

I would use react-motion like this:

<Motion style={{currentOpacity: spring(this.state.open ? 1 : 0, { stiffness: 140, damping: 20 })}}>
    {({currentOpacity}) =>
        <div style={{opacity: currentOpacity}}>
            <BasketContents />
        </div>
    }
</Motion>

I haven't tested it, but it should work.

Upvotes: 6

Related Questions