This fixes two bugs related to string-casting in React:
# Setting `<input value={0} />` would use an empty `value` because `0` is falsey.
# Using `onChange` and `setState` with non-strings could lead to an infinite loop.
The latter is possible with controlled inputs when:
- User changes input value.
- `onpropertychange` fires.
- `ChangeEventPlugin` dispatches `onChange`.
- A handler responds via `this.setState` with a non-string value (e.g. a number).
- The input re-renders and re-sets `value`.
- The new `value` is not a string, but the current `value` (read from the element) is cast to a string automatically by the browser.
- This triggers another `onpropertychange`.
- `ChangeEventPlugin` dispatches another `onChange`.
- ...
This is a bit unfortunate, but it'll shut lint up for the time being. We
can't just change the modules to use `module.exports = { ... }` due to
how we handle circular dependencies internally (`ReactMount` require
`ReactID` and vice versa).
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.
This is a micro-optimization that reduces the lookup time for missing lifecycle methods. The extra amount of memory is linear to the number of components that exist on a page which I think is a worthwile trade-off.
When `ReactNativeComponent` updates, it calls `updatePropertyByID` in `ReactDOMIDOperations` which calls `DOMPropertyOperations`. However, in `ReactDOMIDOperations`, we will lookup the node for an ID using `ReactID.getNode`. This wastes time looking for nodes when we may not need to ever update it (e.g. `children`).
This changes `ReactNativeComponent` to bail out sooner.
This adds two new warnings and one new invariant:
- Warn when using React.autoBind() that it is deprecated.
- Throw when calling bind() on an autobound method with the wrong value of "this". Today we'll silently ignore the provided value, which is confusing.
- Warn when calling bind() on an autobound method with the *right* value of "this" and no other arguments. This is already done for you by React.
We're missing a bunch of elements. So I scraped them from https://developer.mozilla.org/en-US/docs/Web/HTML/Element. Here's the script I used (run from Firefox scratchpad):
```
Array.prototype.slice.call(document.querySelectorAll('div.index.widgeted li'))
.filter(function(li) {
return !/deprecatedElement|obsoleteElement|nonStdElement/.test(li.firstChild.className)
})
.map(function(li) {
// <tag> -> tag
return li.querySelector('code').textContent.replace(/<(.+)>/,'$1');
})
.join(': false,\n ');
```
I had to filter a couple more out (because there's some malformed content), but then it was simply merge with what we had and check to see if the new ones needed to omit the close tag.
This fixes our perf test by coping with edge cases like the
injection of `<tbody>` between `<table>` and `<tr>` nodes, which occurs
automatically in some browsers when we set `.innerHTML`.
Introducing more search branches would be risky if not for my previous
commit that made `findComponentRoot` breadth-first instead of depth-first.
This function needs to be as fast as possible for those cases when
`ReactID.getNode` can't rely on the `nodeCache`.
Breadth-first search prevents us from diving too deeply down the wrong
branches when the sought-after node can be found at a shallower level.
The queue required for breadth-first search is implemented by a single
array indexed by `childIndex`. To save space, only the `.firstChild` nodes
are stored, and we use `.nextSibling` to iterate over the other siblings
in a `while` loop.
When we render a new component into a container, we now record a reference to the rendered DOM node as `rootElementsByReactRootID[reactRootID]`, so that we can determine the actual container later on, in case `containersByReactRootID[reactRootID]` is no longer the true container.
If any component throws during reconciliation any subsequent reconciliation will break badly because ReactUpdates will be in an inconsistent state.
Add a finally block to prevent this.
Whenever a component is unmounted, we delete all listeners that might have been attached. This sucks because most applications, Facebook included, do not use every listener. There's a lot of wasted computation, especially if many components are mounted and unmounted.
This changes `deleteAllListeners` to more delete listeners more efficiently.
This fixes a bug with `ChangeEventPlugin` in IE8 and IE9. The extend of this bug includes:
- On IE8, not firing `onChange` immediately after `value` of an input is changed.
- On IE9, not firing `onChange` when backspacing.
Introduces a counterpart to mapChildren. It excludes empty children just as
mapChildren for compatibility. With might introduce something like
sliceChildrenIncludingEmptyValues at some point.
I still think the semantics of flattening children is valid but we'll
want to revert the flattening implementation while we solidify the
semantics and try another approach.
This reverts flattening so that this.props.children can once again be
either a single child, flat array or nested array.
mapChildren calls flattenChildren before doing anything else. This is
not the most efficient approach but I wanted to keep this inital diff
simple. It also ignores empty values for backwards compatibility.
We may want to try another approach where empty values are included
in the map.
Validation of keys is still done inside ReactComponent. Ideally I'd
like to extract that into a separate module but to avoid cyclic
dependencies, I'm keeping it in ReactComponent for now.
This fixes a race condition if the `onClick` tries to update the input or textarea (e.g. by calling `setState`):
<input
onClick={function(event) {
this.setState({somethingElse: true}); // Triggers an update.
// event.target.value is now equal to the old value, fail...
this.props.onChange(event);
})
/>
A dynamic value can be provided as a key to a child. Either as part of an object
or key property. This becomes part of the component's ID.
We have to be careful to escape this key before inserting it into the DOM since
it could become a vulnerability. We fixed this by escaping just the keys.
However, the current implementation breaks when you used escaped keys. The
internal value is escaped and the value used by getAttributeNode and
getElementById are both unescaped.
This fixes that by keeping the unescaped value internally but escaping it right
before the HTML is generated (like any other attribute).
This is important since business logic IDs (that should be used as keys)
contains characters that need to be escaped.
This changes `ReactDOMTextarea` to accept `defaultValue` and `value`. It will warn people about using children (but allow it and treat it as `defaultValue`, which is the current behavior).
Instead of simply logging the React ID of the `ancestorNode` when `findComponentRoot` fails, use a `try ... finally` to `console.error` the `ancestorNode`. This allows modern web inspectors to actually log a reference to the node (which may not have a React ID).
This means when people run into the problem, they will not have to execute:
require('ReactID').getNode(<copy+paste>);
NOTE: Admittedly, this will not log anything in IE8. That's fine, since IE8 has shitty console logging anyway.
Just a bit of byte savings for server rendering. Props to @benjamn for the base36 idea (and for making this diff easy).
With a little work we could probably get rid of the .r as well.
This changes React to throw when `ReactID.getNode()` fails to find a node. This method is used by two call sites:
- Implements `ReactComponent#getDOMNode`. This method already throws if a component is not mounted, and //all mounted components should be able to find their rendered root nodes//.
- Used by `ReactDOMIDOperations`. These call sites aleady assume that `getNode` returns a non-null. Currently, if the node is not found, this is the site that fatals (and the stack trace is much harder to debug).
The error message should make it //a lot// easier to debug unexpected DOM trees. In particular, this will help track down all the places where the browser inserts `<tbody>` unexpectedly.