我正在尝试使用CSSTransitionGroup为SentenceList中的Sentences制作动画。当按下“下一个”按钮时,我希望下一个句子动画,列表中的第一个按钮淡出。但是我收到此错误消息:
数组中的每个子节点都应该有一个唯一的“键”支柱。检查 句子的渲染方法。
我不明白为什么会这样,因为当我将Sentence推入我的列表时,我将{sentence.id}作为“关键”道具传递给我。 React不应该知道每个句子键在渲染时都是这样定义的吗?
我已经尝试在Sentence渲染方法中再次定义键但无济于事。我的状态变化是否使React失去对当前句子键的追踪?
感谢您的帮助!
SentenceList:
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
var SentenceList = React.createClass({
getInitialState: function() {
return {
sentences: this.props.sentences
}
},
//receives sentence and new blip from Sentence
addBlip: function(sentence, value) {
//see where in the loaded sentences we are
var i = this.state.sentences.indexOf(sentence),
sentences = this.state.sentences,
// callback within a callback (post), the context changes inside the callback so we need to set this to self
self = this;
$.post(
'/sentences/' + sentence.id + '/blips',
{blip: {body: value}},
//set sentence we blipped into as answered
//reset state to reload sentences state after post
function(response) {
sentences[i].answered = true;
// sentences[i].statistics = response.statistics;
// put dummy content first then work it out in the backend to receive the format you want to receive (better to work from front to back)
sentences[i].statistics = [
{word: "butts", frequency: "95%"},
{word: "dogs", frequency: "2%"},
{word: "vegetables", frequency: "1%"},
{word: "sun", frequency: "2%"}
];
self.setState({sentences: sentences});
});
},
//take in a sentence (sent from Sentence) and find current position in loaded sentences and set it to dismissed, then reload list
dismissSentence: function(sentence) {
var i = this.state.sentences.indexOf(sentence),
sentences = this.state.sentences;
sentences[i].dismissed = true;
this.setState({sentences: sentences});
},
//list undismissed sentences and take out the first 3 for display
topThreeRemainingSentences: function() {
var unanswered = _.where(this.state.sentences, {dismissed: false});
return unanswered.slice(0, 3);
},
render: function() {
var remaining = this.topThreeRemainingSentences(),
sentences = [],
index = 0;
//loop through sentences until we have 3 remaining sentences loaded
while (index <= (remaining.length - 1)) {
var sentence = remaining[index];
sentences.push(
<Sentence key={sentence.id}
isActive={index == 0}
isNext={index == 1}
isNnext={index == 2}
onDismiss={this.dismissSentence}
onSubmitBlip={this.addBlip}
details={sentence} />
)
index = index + 1;
}
return (
<ReactCSSTransitionGroup transitionName="animate">
<div>{sentences}</div>
</ReactCSSTransitionGroup>
)
}
});
句:
var Sentence = React.createClass({
getDefaultProps: function() {
return {
onSubmitBlip: function() { console.log(arguments) }
}
},
//pass sentence and new blip to submit function
addBlip: function(e) {
e.preventDefault();
var blipBody = this.refs.newBlip.getDOMNode().value
this.props.onSubmitBlip(this.props.details, blipBody);
},
//send sentence to List to set it to dismissed
dismissSentence: function(e) {
e.preventDefault();
this.props.onDismiss(this.props.details);
},
render: function() {
var phrase = this.props.details.body,
phrase_display = phrase.split("*"),
before = phrase_display[0],
after = phrase_display[1],
positionClass,
stats;
if (this.props.isActive) {
positionClass = "active-sentence"
} else if (this.props.isNext) {
positionClass = "next-sentence"
} else if (this.props.isNnext) {
positionClass = "nnext-sentence"
}
//find stats for sentence if answered from json and push them into array ["word", x%]
if (this.props.details.answered) {
var words = [];
this.props.details.statistics.forEach(function(statistic) {
words.push(<li className="stats-list"><span className="stats-list-word">{statistic.word} </span>
<span className="stats-list-percent">{statistic.frequency} </span> </li>)
})
stats = <div><span className="stats-list-header">others said:</span> {words}</div>
}
if (this.props.isActive) {
nextButton = <div className="next-button" onClick={this.dismissSentence}>V</div>
}
if (this.props.isNext) {
nextButton = <div></div>
}
if (this.props.isNnext) {
nextButton = <div></div>
}
return (
<div className={"blipForm " + positionClass}>
{before}
<form onSubmit={this.addBlip}>
<input type="text"
ref="newBlip" />
</form>
{after}
{nextButton}
<br/>
<ul>{stats}</ul>
</div>
)
}
});
答案 0 :(得分:0)
在Sentence组件的<li>
方法中创建的render
元素需要key
属性:
this.props.details.statistics.forEach(function(statistic) {
words.push(<li className="stats-list" key={statistic.id}>...</li>);
});