Do you want to request a feature or report a bug?
bug
What is the current behavior?
If props.children has single node, all behavior is right.
But props.children has multiple nodes, unexpected unmount/mount has occurred on unchanged node when delete other node.
minimal demo of the problem
props.children has single node)https://jsfiddle.net/sheile/pyuqmytj/1/
When click "toggle" button, toggle <span>loading</span> and don't affect on <Dummy/> node.
props.children has multiple nodes)https://jsfiddle.net/sheile/pyuqmytj/2/
i.e. Add <h1> node to previous fiddle.
When click "toggle" button, toggle <span>loading</span> and unmount <Dummy/> and mount new <Dummy/> node.
note: key attribute is helpless about this problem.
What is the expected behavior?
Keep <Dummy/> node regardless number of children.
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
Reproduced by react 0.14.8, 15.4.2 and 16.0.0-alpha.
@Sheile thanks for the report. I was able to verify this behavior. At a quick glance I think its a bug with our child reconciler. The IDs we generate for the children are not consistent when we update them:

Since the IDs don't match ReactChildReconciler ends up mounting the child again and the old one gets queued up to be unmounted. I'm not very familiar with child reconciliation but this seems like unexpected behavior (cc @gaearon what do you think?) This might just be a consequence of returning a different root element (even if it's the same type).
@Sheile you can get around this by inlining your conditional render
<div>
{this.props.children}
{this.props.loading && <span>Loading</span>}
</div>
Could this be the same as the first issue I ever reported? 馃槃
https://github.com/facebook/react/issues/1493
I don't think this is necessarily a bug, but I鈥檓 not sure.
Here鈥檚 why I think it happens. Looking at the second fiddle, in the if branch, div's children end up being an array with two elements (Dummy and span). However, in the else branch, div's children is actually a single child: Dummy. React optimizes this case and doesn鈥檛 produce an extra array when a JSX element only has a single child. So when we toggle the button, we switch between an array with one element to just that element. React currently treats it as a change in type, although I鈥檓 not sure it鈥檚 right.
In any case, this is very easy to work around:
class Loader extends React.Component {
render() {
return (
<div>
{this.props.children}
{this.props.loading &&
<span>Loading</span>
}
</div>
)
}
}
Just don鈥檛 rely on matching up "holes" between different branches.
https://jsfiddle.net/8rnmsqtv/
This is an unfortunate side-effect of Reacts special logic that exists to counter the effects where JSX does not wrap an only child in an array (what @gaearon linked to). I.e. React treats child === [child], but [child] !== [[child]]. So since you are inserting this.props.children adjacent to another element it becomes nested and the special logic stops working as you're now effectively encountering the [child] !== [[child]] case. IMHO I think putting anything adjacent to this.props.children isn't very nice and should be avoided (thus avoiding this problem too).
On another note, it would be interesting if there was a neat way for JSX to just mark an only child so that React can apply the special logic only and always in that case. Thus entirely eliminating this unintuitive and magic behavior from React. But I don't think there is.
EDIT: I wonder, wouldn't {React.Children.toArray(this.props.children)} (as well as map, etc) actually side-step this? It should. Which would further highlight the oddities of this special logic and perhaps something we should highlight as a necessity when inserting this.props.children adjacent to other elements.
EDIT2: Perhaps the DEV performance penalty is too high, but we could emit a warning when you encounter this case... although that would require DEV-mode for JSX too which seems like a no-go.
EDIT3: https://github.com/facebook/react/issues/2378 is a possible solution, but perhaps the side-effects are equally bad when you're mixing children like this (haven't thought too much about it).
Thank you for pointing related issues and taking time to explain it.
I have understood this problem and workaround.
Yesterday, I tried to flatten children in ReactElement.createElement when children is Array.
This problem has been resolved, but need additional key attribute to identify node that in different nest level like this.
https://github.com/Sheile/react/commits/test/unexpected-unmount
hmm.. this problem is more than I can handle.
Please close this issue if no longer need discussion.
Going to close as wontfix for now. Can come back later.
Most helpful comment
Could this be the same as the first issue I ever reported? 馃槃
https://github.com/facebook/react/issues/1493
I don't think this is necessarily a bug, but I鈥檓 not sure.
Here鈥檚 why I think it happens. Looking at the second fiddle, in the
ifbranch,div's children end up being an array with two elements (Dummyandspan). However, in theelsebranch,div's children is actually a single child:Dummy. React optimizes this case and doesn鈥檛 produce an extra array when a JSX element only has a single child. So when we toggle the button, we switch between an array with one element to just that element. React currently treats it as a change in type, although I鈥檓 not sure it鈥檚 right.In any case, this is very easy to work around:
Just don鈥檛 rely on matching up "holes" between different branches.
https://jsfiddle.net/8rnmsqtv/