I am trying to use CSSTransitionGroup to animate Sentences in a SentenceList. When the "next" button is pressed I want the next Sentence to animate in and the 1st in the list to fadeout. However I get this error message:
Each child in an array should have a unique "key" prop. Check the render method of Sentence.
I don't understand why that is since when I push Sentence into my List I am passing it a {sentence.id} as a "key" prop. Shouldn't React know that each sentence key is defined as such when rendering it?
I've tried defining the key again in the Sentence render method but to no avail. Are my State changes making React lose track of the current Sentence key?
Thanks for your help!
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>
)
}
});
Sentence:
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>
)
}
});
Aucun commentaire:
Enregistrer un commentaire