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

时间:2015-06-25 20:15:57

标签: javascript timer reactjs

更新

我自己在下面回答,但是可能会提供一个更“优雅的思考”的更优雅的解决方案..

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

到目前为止,这么好。我已经让组件工作,以便状态更改正好管理className分配,标题(我的代码中的“短语”)一次动画一个字母,并按顺序循环每个短语,然后重置。

但是,我想要做的是在推进字母时有300ms的定时器间隔,并且当有一个新的短语时暂停2000ms。

目前,尽管我试图在循环新短语时设置更长的animationDelay状态(参见下面代码中的[0a]),但componentDidMount()中的计时器间隔永远不会改变。为什么是这样?如何让componentDidMount()使用当前更改的state.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;

1 个答案:

答案 0 :(得分:1)

好的,所以最后,实际上这只是我在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();
        }
    },