React componentDidMount()中使用条件计时器间隔不起作用



更新

我在下面自我回答,不过这是一个更优雅的解决方案;React中的思考;可能可以提供


我正在CodyHouse创建这个"动画标题"jQuery小部件的React版本:http://codyhouse.co/demo/animated-headlines/index.html(点击"键入")

到目前为止,一切都很好。我已经让组件工作起来,这样状态更改就可以很好地管理className分配,标题(我代码中的"短语")一次以一个字母为动画,并按顺序循环每个短语,然后重置。

然而,我想做的是,当在字母中前进时,有一个300ms的定时器间隔,当有一个新的短语时,有2000ms的暂停。

目前,尽管我试图在循环使用新短语时设置更长的animationDelay状态(请参阅下面代码中的[0a]),componentDidMount()中的计时器间隔从未改变。为什么会这样?如何让componentDidMount()使用当前更改的状态。animationDelay?

注意:您可以忽略下面的大部分代码。只有注释行和componentDidMount()有问题。为了完整起见,我将粘贴其余部分。

var React = require('react'),
    ClassNames = require('classnames');
var AnimatedPhrases = React.createClass({
    
    getInitialProps: function () {
        
        return {
            animateType: 'type'
        }
    },
    
    getInitialState: function () {
        
        return {
            animationDelay:300,
            visible: 0,
            currentLetter: 0,
            currentPhrase: 0,
            phraseCount: this.props.phrases.length
        }
    },
    
    componentDidMount: function(){
        this.timer = setInterval(this.tick, this.state.animationDelay);
    },
    componentWillUnmount: function(){
        clearInterval(this.timer);
    },
    
    isLastLetter: function () {
        
        var phrases = this.props.phrases;
        var currentPhraseLength = phrases[this.state.currentPhrase].name.length;
        
        return (this.state.currentLetter === currentPhraseLength-1);
    },
    
    isLastPhrase: function () {
        
        var phrases = this.props.phrases;
        var currentPhraseLength = phrases[this.state.currentPhrase].name.length;
        
        return (this.state.currentPhrase === this.state.phraseCount-1 
            && this.state.currentLetter === currentPhraseLength-1);
    },
    tick: function(){
        
        var phrases = this.props.phrases;
        var currentPhraseLength = phrases[this.state.currentPhrase].name.length;
        
        // if we are on the last letter of the current phrase, we need
        // to increment the current phrase at the next pass [0] and
        // pause for longer [0a]
        // unless it's the last phrase
        // in which case we reset the current phrase and letter. [1]
        // If we are in the middle of a word, continue to increment 
        // only the current letter [2]
        if (this.isLastPhrase()) {            
            this.setState({
                currentPhrase: 0, // [1]
                currentLetter: 0, // [1]
            });
        }
        if (this.isLastLetter()) {
            this.setState({
                currentLetter: 0, // [0]
                currentPhrase: this.state.currentPhrase+1, // [0]
                animationDelay: 2000 // [0a]
            });
        } else {
            this.setState({
                currentLetter: this.state.currentLetter+1 // [2]
            });
        }
    },
    
    buildPhrase: function (phrase, index) {
                
        var isVisible = this.state.currentPhrase == index ? true : false;
        
        var classes = ClassNames({
            'is-visible': isVisible,
            'is-hidden': !isVisible
        });
        var text = '';
        
        if ( phrase.hasOwnProperty('name') ) {
                   
            text = phrase.name;
            
            if (isVisible) {
                // cycle through letters and create an <i> per letter
                // with class depending on matching the current letter
                var splitPhrase = this.singleLetters(text);
        
                if (phrase.hasOwnProperty('url') && phrase.hasOwnProperty('id')) {
                    return <a className={ classes } key={index} href={phrase.url}>{ splitPhrase }</a>
                } else {
                    return <b className={ classes } key={index}>{ splitPhrase }</b>
                }
            }
        }
    },
    
    singleLetters: function (phrase, isVisible) {
        
        var currentLetter = this.state.currentLetter;
        var letters = phrase.split("");
        var letterCount = phrase.length;
        
        var newLetters = letters.map(function (letter, index) {
            return <i className={ currentLetter >= index ? 'in' : 'out' } key={index}>{letter}</i>
        });
        
        return newLetters;
    },
    
    render: function() {
            
        var buildPhrase =  this.buildPhrase;
        
        phrases = this.props.phrases.map(function (phrase, index) {
            return buildPhrase(phrase, index);
        });
        
        var classes = ClassNames('animated-phrase', this.props.animateType);
        
        return (
            <h1 className="animated-phrase letters type">
                <span>{this.props.headline}</span>
                <span className="word-wrapper waiting">
                    { phrases }
                </span>
            </h1>
        )
    }
});
module.exports = AnimatedPhrases;

好的,所以最后,实际上这只是我在tick()中设置状态后清除计时器的一个例子。如果有人知道更优雅的解决方案(很可能!)请发布:)

tick: function(){
        var phrases = this.props.phrases;
        var currentPhraseLength = phrases[this.state.currentPhrase].name.length;
        // if we are on the last letter of the current phrase, we need
        // to increment the current phrase at the next pass [0]
        // unless it's the last phrase
        // in which case we reset the current phrase and letter [1]
        // if we are in the middle of a word, continue to increment 
        // only the current letter [2]
        if (this.isLastPhrase()) {            
            this.setState({
                currentPhrase: 0, // [1]
                currentLetter: 0, // [1]
            });
        }
        if (this.isLastLetter()) {
            this.setState({
                currentLetter: 0, // [0]
                currentPhrase: this.state.currentPhrase+1, // [0]
                animationDelay: 2000
            });
            // this fixes it!
            clearInterval(this.timer);
            this.componentDidMount();
        } else {
            this.setState({
                currentLetter: this.state.currentLetter+1, // [2],
                animationDelay: 300
            });
            clearInterval(this.timer);
            this.componentDidMount();
        }
    },

最新更新