The original tests were flawed because the `Danger` module exploits the fact that all React-generated markup has at least one attribute. This allows the module to extract node names from markup strings faster.
However, the tests were passing in strings of markup with no attributes.
Also, this fixes a test failure due to the test trying to set text content into a `<tr>` which is typically disallowed by browsers (and PhantomJS). This changes it to use `<td>` instead.
Not all testing environments will support setting the `innerHTML` descriptor. For example, PhantomJS initializes the `innerHTML` property as not configurable.
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.
As @leebyron and balpert pointed out, if the markup on the server is differnet than what the client expects undefined behavior and chaos may ensue. A good fallback
is for us to just inject the client-side markup (as it is the source of truth) and warn the user in __DEV__ that something is wrong. In order to do a fast
browser-independent check of the DOM I use an adler32 checksum of the generated markup. I believe this is better than a simple innerHTML compare because different
browsers massage innerHTML differently.
This works around a bug with listening to clicks using event delegation on Mobile Safari using an event plugin.
NOTE: We don't enable touch events by default, so I don't know if would want to inject this plugin by default. In fact, I'm not sure what our strategy is at all for when to invoke `React.useTouchEvents(true)`.
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.
This was not necessary when we were running each test in its own
`<iframe>`, and it doesn't seem to affect any test behavior currently, but
it seems wise for the sake of test isolation and hygiene.
We don't sync upstream polyfills (because we don't have a story for how
they would be used), so this needs to be updated manually.
Sacrificed some negligible performance optimizations to reduce the number
of different cases from four to one.
It's important to test this implementation in PhantomJS, since that's the
only browser that I know of where built-in functions sometimes do not have
a `.prototype`.
This cuts the running time of `grunt phantom:run` from 4.4s to 3.1s on my
machine, because we no longer have to load/execute a separate instance of
`react-test.js` in a separate `<iframe>` for each test.