简体   繁体   中英

Toggling a CSS class when a React.js component gets updated

I made a simple React.js stateful component displaying a counter with numbers updating every second. I would like to implement a scroll effect to this counter, making the new digit scrolling down from the top every time the counter increases. Please check my pen on Codepen to see how the project looks like. My question regards specifically the digit you will see there on the right side of the counter. You will notice that there are two digits one on top of each other, the digit to be showed next (binded to the component state) is positioned above the digit currently visible. Both digits are positioned with CSS transform property, now what I want to do is to translate those digits on their Y axis every time new number increases. Here is the code of the component:

var ReactCounter = React.createClass({
newFirstDigit: 0,
currentFirstDigit: 0,
currentSecondDigit: 0,  
scrollFirstDigit: false,

// Set initial state with counter not running
getInitialState: function() {
    return {
        running: false,
        newSecondDigit: 0,
        scrollSecondDigit: false,
    }
},

// Called as soon as Counter is added to the DOM
componentDidMount: function() {
    this.interval = setInterval(this.timeCounter, 1000);
},

// Called as soon as Counter is removed from the DOM
componentWillUnmount: function() {
    clearInterval(this.interval);
},

// Called as soon as Counter is updated
componentDidUpdate: function() {
    if (this.currentSecondDigit != this.state.newSecondDigit) {
        // add scroll class
        this.setState ({
            scrollSecondDigit: true,
        })
        this.currentSecondDigit = this.state.newSecondDigit;
    }

    if (this.currentSecondDigit == this.state.newSecondDigit) {
        // remove scroll class
        this.setState ({
            scrollSecondDigit: false,
        })
    }

    if (this.currentFirstDigit !== this.newFirstDigit) {
        // add scroll class
        this.scrollFirstDigit = true;
        this.currentFirstDigit = this.newFirstDigit;
    }

    if (this.currentFirstDigit === this.newFirstDigit) {
        // remove scroll class
        this.scrollFirstDigit = false;
    }

    if (this.state.newSecondDigit == 10) {
        this.newFirstDigit++;
    }

    if (this.state.newSecondDigit == 10) {
        this.setState({
            newSecondDigit: 0,
        });
        this.currentSecondDigit = 0;
    }

    // Stop and reset counter when it gets to 60
    if (this.newFirstDigit == 6) {
        if (this.state.newSecondDigit == 0) {
            this.setState({ 
                running: false,
                newSecondDigit: 0
            });
            this.newFirstDigit = 0;
            this.currentFirstDigit = 0;
            this.currentSecondDigit = 0;
        }
    }
},

timeCounter: function() {
    if (this.state.running) {
        this.setState({
            newSecondDigit: this.state.newSecondDigit + 1,  
        });
    }
},

startCounter: function() {
    this.setState({ 
      running: true,
      previousTime: Date.now(),
    });
},

stopCounter: function() {
    this.setState({ 
        running: false
    });
},

resetCounter: function() {
    this.setState({
      newSecondDigit: 0,
      previousTime: Date.now(),
    });
    this.newFirstDigit = 0;
    this.currenFirstDigit = 0;
    this.currentSecondDigit = 0;
},

render: function() {
    var scrollFirstDigit = this.scrollFirstDigit === true ? 'scroll' : '';
    var scrollSecondDigit = this.state.scrollSecondDigit === true ? 'scroll' : '';

    return (
        <div className="counter-container">
            <div className="numbers-container">
                <div className="number-container one">
                    <div className={"number current " + scrollFirstDigit}>
                        {this.newFirstDigit}
                    </div>
                    <div className={"number previous " + scrollFirstDigit}>
                        {this.currentFirstDigit}
                    </div>
                </div>
                <div className="number-container two">
                    <div className={"number current " + scrollSecondDigit}>
                        {this.state.newSecondDigit}
                    </div>
                    <div className={"number previous " + scrollSecondDigit}>
                        {this.currentSecondDigit}
                    </div>
                </div>
            </div>
            <div className="buttons-container">
                {this.state.running ? <button className="btn stop" onClick={this.stopCounter}>Stop</button> : <button className="btn start" onClick={this.startCounter}>Start</button> }
                <button className="btn reset" onClick={this.resetCounter}>Reset</button>
            </div>
        </div>
    ); 
}

})

ReactDOM.render( <ReactCounter />, document.getElementById('app-container') );

You will see I am trying to add and remove the CSS class in the componentDidUpdate method. I also tried that in the componentWillUpdate method, however both solutions do not work correctly. What is in your opinion the best way to implement the desired scroll effect?

Based on what I can understand you want to change the CSS class based on the scrollFirstDigit and scrollSecondDigit variables you've created (adding a scroll class to the list of classes). In React I often use the classnames modules to do things like this. For example the following maybe something you're interested in:

var classnames = require('classnames');
...
<div className={classnames({ 'number current': true, 'scroll': this.state.scrollFirstDigit === true})}>;

So you can simply toggle the CSS classes you want by altering the state object (since it will trigger a re-render and cause the revaluation of the classes). You can see more examples at the modules github page.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM