Allow more than strings and numbers to be used as attributes for DOM
nodes. This removes the special casing for `0` and `false` that was
being used in ReactDOMInput and ReactDOMTextarea.
Now we will just `toString` any object we try to insert into a DOM.
Closes#422, #372, #302
This introduces `ReactLink` which is a super lightweight way to do two-way binding for React.
If you want to use a controlled form input today, it's a lot of lines of code:
http://jsfiddle.net/T3z3v/
Look how many times `name` is repeated in there. And you have to remember to wire up event handles and pass the right state and
right event handler for *each* form field. It's really annoying.
With ReactLink, you can "link" a form value to a state field. It's just some simple sugar around the value prop/onChange
convention:
https://gist.github.com/petehunt/6689857
Ah, much nicer! And requires very little core changes or extra bytes. `ReactLink` just wraps the current value and "request
change" handler into a little object and provides some sugar to create some from composite component state.
Previously, setting textarea `value` to number 0 is treated as if `value` wasn't set at all (thus the textarea is cleared from 0 to '' upon `onChange`). `false` also renders as `"false"` instead of `""` for both `defaultValue` and `value`, on textarea _and_ input.
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 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);
})
/>
Upgrade `TextChangeEventPlugin` to be the `onChange` event that React
fires. In React, `onChange` will now fire when `input` fires for form elements in
modern browsers.
Handle this for:
input[type=text]
input[type=password]
input[type=checkbox]
input[type=radio]
textarea
select
Support:
- OSX Chrome
- OSX Safari
- OSX Firefox
- Win 7 / IE8
- Win 7 / IE9
- Win 7 / IE10
Everything works but caret selection / placement differs from browser to
browser.
For <select> elements, the event is fired with `change`. This is a
conscious decision, even though in some browsers (OSX firefox, IE), it
can be argued that the event should fire more due to how the UI looks.
Builds on https://github.com/facebook/react/pull/75, which handled only
text inputs.
Summary:
This is a proposal based loosely on the discussions I've had with @paulshen and @jwalke. It implements a shim for `React.DOM.input` (used as `<input>`) that supports two different use cases depending on whether `value` is provided or not.
If a `value` is //not// provided, the input will be initialized with the empty string (or `defaultValue`) and anytime the user changes the input, the `onChange` (or `onTextChange`) handler will be fired and the DOM will reflect the new changes.
React.renderComponent(
<input type="text" defaultValue="Untitled" onTextChange={handleChange} />,
container
);
If a `value` is provided, the input will be initialized to that value. Anytime the user changes the input, the `onChange` (or `onTextChange`) handler will be fired. However, the DOM will //not// reflect the new changes. If a `value` is provided, it is the responsibility of the owner to update the `value` prop passed in.
var value = "Untitled";
var input = React.renderComponent(
<input type="text" value={value} onTextChange={handleChange} />,
container
);
function handleChange(event) {
// Do something cool like strip out non-numbers.
var value = event.target.value.replace(/\D/g, '');
input.setProps({value: value});
}
This is just a start and we should build similar components for `textarea` and `select`. Also, this does not inject the new components because the changes are not backward compatible. Once we change all `<input>` uses to use `ReactDOMInput`, then we can inject.