First render looks (roughly) like this:
<div>
<div key={0}><input defaultValue="1" /></div>
<div key={1}><input defaultValue="2" /></div>
<div key={2}><input defaultValue="3" /></div>
</div>
<div>
<div key={0}><span>1</span></div>
<div key={1}><span>2</span></div>
<div key={2}><span>3</span></div>
</div>
Cool, no problems. Then let's say I put 'foo' in the box and click Add Value. The state updates by inserting it before the first item (side note: splice(0,0,x) is unshift(x)), you then render this:
<div>
<div key={0}><input defaultValue="foo" /></div>
<div key={1}><input defaultValue="1" /></div>
<div key={2}><input defaultValue="2" /></div>
<div key={3}><input defaultValue="3" /></div>
</div>
<div>
<div key={0}><span>foo</span></div>
<div key={1}><span>1</span></div>
<div key={2}><span>2</span></div>
<div key={3}><span>3</span></div>
</div>
Now it's time for React to take these two, and figure out what changed. To do this it compares the component (div, span, or input here), and the keys.
Starting with the span section, it sees that all the tags are the same, but there's a new key it hasn't seen before at the end. It also sees that the value of div[0] span
, div[1] span
, and div[2] span
have all changed. It inserts the new elements with the text 3 at the end, and updates the other spans. Inefficient, but it works.
Now for the inputs... it does roughly the same thing. The keys and tags for the first three inputs are the same, it tells each of the inputs that they're updating, they check if they have a value prop, and if not, kindly deny to do anything.
It sees that there's a new input at the end. It is at div[3] input
and has a defaultValue of "3". React inserts the new input, sets the value to 3, and the update is complete.
As you continue, the above procedure is the same, and it continues updating the spans, and inserting a new div span
and div input
each time.
The main problem here, aside from defaultValue which I don't think should ever be used, is the keys are upside down! Items are being inserted at the beginning, so keys should descend, not ascend. This can be fixed by using the length of the items and subtracting the index from that. If length is 4, the keys will be 4, 3, 2, 1.
this.state.app.data.map(function(page, i, items){
return(
<div key={items.length - i}>{self.renderElement(page)}</div>
);
})
React then says 'oh, there's a new item at the beginning, I should just insert a node there', and everyone's happy. The end.