更新
我在下面自我回答,不过这是一个更优雅的解决方案;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();
}
},