Skip to content

Commit

Permalink
Batch Child Markup Generation
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
yungsters authored and zpao committed Jul 26, 2013
1 parent 2e37f65 commit adffa9b
Show file tree
Hide file tree
Showing 11 changed files with 666 additions and 452 deletions.
15 changes: 10 additions & 5 deletions src/core/ReactDOMIDOperations.js
Expand Up @@ -172,12 +172,17 @@ var ReactDOMIDOperations = {
},

/**
* TODO: We only actually *need* to purge the cache when we remove elements.
* Detect if any elements were removed instead of blindly purging.
* Updates a component's children by processing a series of updates.
*
* @param {array<object>} updates List of update configurations.
* @param {array<string>} markup List of markup strings.
* @internal
*/
manageChildrenByParentID: function(parentID, domOperations) {
var parent = ReactMount.getNode(parentID);
DOMChildrenOperations.manageChildren(parent, domOperations);
dangerouslyProcessChildrenUpdates: function(updates, markup) {
for (var i = 0; i < updates.length; i++) {
updates[i].parentNode = ReactMount.getNode(updates[i].parentID);
}
DOMChildrenOperations.processUpdates(updates, markup);
}

};
Expand Down
14 changes: 12 additions & 2 deletions src/core/ReactInstanceHandles.js
Expand Up @@ -229,14 +229,24 @@ function traverseParentPath(start, stop, cb, arg, skipFirst, skipLast) {
*/
var ReactInstanceHandles = {

separator: SEPARATOR,

createReactRootID: function() {
return getReactRootIDString(
Math.ceil(Math.random() * GLOBAL_MOUNT_POINT_MAX)
);
},

/**
* Constructs a React ID by joining a root ID with a name.
*
* @param {string} rootID Root ID of a parent component.
* @param {string} name A component's name (as flattened children).
* @return {string} A React ID.
* @internal
*/
createReactID: function(rootID, name) {
return rootID + SEPARATOR + name;
},

/**
* Gets the DOM ID of the React component that is the root of the tree that
* contains the React component with the supplied DOM ID.
Expand Down

0 comments on commit adffa9b

Please sign in to comment.