Setting `innerHTML` is slow: http://jsperf.com/react-child-creation/2
This reduces the number of times we set `innerHTML` by batching markup generation in a component tree.
As usual, I cleaned up the `ReactMultiChild` module significantly.
== Children Reconciliation ==
When a `ReactNativeComponent` reconciles, it compares currently rendered children, `prevChildren`, with the new children, `nextChildren`. It figures out the shortest series of updates required to render `nextChildren` where each update is one of:
- Create nodes for a new child and insert it at an index.
- Update an existing node and, if necessary, move it to an index.
- Remove an existing node.
This serializable series of updates is sent to `ReactDOMIDOperations` where the actions are actually acted on.
== Problem ==
There are two problems:
# When a `ReactNativeComponent` renders new children, it sets `innerHTML` once for each contiguous set of children.
# Each `ReactNativeComponent` renders its children in isolation, so two components that both render new children will do so separately.
For example, consider the following update:
React.renderComponent(<div><p><span /></p><p><span /></p></div>, ...);
React.renderComponent(<div><p><img /><span /><img /></p><p><img /><span /><img /></p></div>, ...);
This will trigger setting `innerHTML` four times.
== Solution ==
Instead of enqueuing the series of updates per component, this diff changes `ReactMultiChild` to enqueue updates per component tree (which works by counting recursive calls to `updateChildren`). Once all updates in the tree are accounted for, we render all markup using a single `innerHTML` set.
There is a circular dependency between `ReactID`, `ReactMount` and
`ReactInstanceHandles`. Ben and I talked about this today. It seems like the
simplest solution is to consolidate a lot of the code that Ben recently wrote
into `ReactMount`. We can later find ways to trim code out of this module
without causing circular deps.
We already skip `null` and `undefined` when building up the stringified html on first render, but if you update a component to the *exact same* conditions, React will leave the DOM in a different state. We shouldn't do that.
When each component unmounts, it already cleans up its respective entry in the node cache. Let's stop blowing away the entire node cache unnecessarily.
This should improve performance because a React component's root will never need to be searched for more than once.
Also removed some unnecessary calls to `document.getElementById`, which
will eventually cease to work for React-specific IDs.
This clears the way for the deprecation of `ReactDOMNodeCache` and
`getDOMNodeID`.
There are currently two places where we lookup and cache nodes: `ReactDOMNodeCache` and `ReactComponent`. Instead, we should just consolidate caches and make `ReactComponent` use `ReactDOMNodeCache`.
The most obvious manifestation of this bug is visible here:
http://jsfiddle.net/spicyj/zzGas/. In short, when props are removed from a
component, the underlying HTML element doesn't have the attribute
removed.
This change should fix it, but unfortunately it (presumably) makes
_updateDOMProperties a bit slower.