Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React toggle class + CSS transitions, not working... why?

I've been using classes to control open/close behaviors w/ a CSS transition for effect. I've used this on other components, no problem, but for some reason the same method is failing me in this scenario...

The open/close behaviors attach (I see the end difference w/ background color and translateY) but the CSS transition itself is lost... any ideas why I lose my CSS transition but everything else is working as expected?

Note, when I manually toggle the open/closed classes using Developer Tools, it works just fine! The CSS transition picks up!

So what's up with the React on click to toggle a class applying, but losing the CSS transition?

class Projects extends React.Component {
    /* constructor, etc... */
    render() {
        return (
            <div className="projects-nav-container">
                <div className="center title monospace" onClick={this.props._toggleProjectNav} id="Menu">Menu</div>
                <ul className={`projects-nav ${this.props._isProjectNavOpen ? 'open' : 'closed'}`}>
                    { PROJECTS.map((project, index) => 
                    <li key={index} >
                         <p>project here</p>
                    </li>
                    ) }
                </ul>
            </div>
        );
    }
}

App.js looks as such:

class App extends React.Component {
    constructor() {
        super();
        this.state = {
            _isProjectNavOpen: true
        }
        this._toggleProjectNav = this._toggleProjectNav.bind(this);
    }
    _toggleProjectNav() {
        this.setState(prevState => ({
            _isProjectNavOpen: !prevState._isProjectNavOpen,
        }));
    }
    render() {
        <div>
            <Router>
                <Route path="/projects" component={(props, state, params) => 
                    <Projects 
                        _toggleProjectNav={this._toggleProjectNav}
                        _isProjectNavOpen={this.state._isProjectNavOpen} 
                    {...props} />} />
            </Router>
        </div>
    }
}

SCSS:

.projects-nav {
    @include transition(all $transition_speed ease);
    &.open {
        @include transform(translateY(0));
        background: red
    }
    &.closed {
        @include transform(translateY(-100vh));
        background: green;
    }
}
like image 461
vesperae Avatar asked Sep 22 '17 20:09

vesperae


People also ask

Can I use CSS transitions in React?

CSSTransition example: Transition a React component using CSS. The CSSTransition component allows you to apply transitions to elements entering and leaving the DOM using CSS. You can achieve this by using the following props: in , a Boolean value used to control the appearance of the element.

How do you trigger transitions in CSS?

To trigger an element's transition, toggle a class name on that element that triggers it. To pause an element's transition, use getComputedStyle and getPropertyValue at the point in the transition you want to pause it. Then set those CSS properties of that element equal to those values you just got.

How do CSS transitions work?

CSS transitions let you decide which properties to animate (by listing them explicitly), when the animation will start (by setting a delay), how long the transition will last (by setting a duration), and how the transition will run (by defining a timing function, e.g., linearly or quick at the beginning, slow at the ...


1 Answers

Indeed, the problem is that react-router is unmounting your component and mounting it again with the new classes, losing the CSS transition in the process. To solve this issue, simply use render instead of component on the <Route> component.

As to why this works, from react-router documentation:

Instead of having a new React element created for you using the component prop, you can pass in a function to be called when the location matches. The render prop receives all the same route props as the component render prop.

For a more detailed explanation, you could read the question react router difference between component and render.

In summary, App.js should look like this:

class App extends React.Component {
    constructor() {
        super();
        this.state = {
            _isProjectNavOpen: true
        }
        this._toggleProjectNav = this._toggleProjectNav.bind(this);
    }
    _toggleProjectNav() {
        this.setState(prevState => ({
            _isProjectNavOpen: !prevState._isProjectNavOpen,
        }));
    }
    render() {
        <div>
            <Router>
                <Route path="/projects" render={(props, state, params) => 
                    <Projects 
                        _toggleProjectNav={this._toggleProjectNav}
                        _isProjectNavOpen={this.state._isProjectNavOpen} 
                    {...props} />} />
            </Router>
        </div>
    }
}

I created a CodeSandbox using render and it seems to work properly!

Cheers!

like image 110
Thiago Murakami Avatar answered Oct 06 '22 00:10

Thiago Murakami