Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de0b0b3705 | ||
|
|
4dec99c61e | ||
|
|
18d715b86e | ||
|
|
a157791264 | ||
|
|
590d82bc33 | ||
|
|
a210dacdf4 | ||
|
|
1dfaa528f8 | ||
|
|
8c811778d6 | ||
|
|
395ae75eb3 | ||
|
|
2606966b43 | ||
|
|
c98f0e6631 | ||
|
|
a7714585bb | ||
|
|
da6e4853be | ||
|
|
afba0f3da7 | ||
|
|
d2b947c351 | ||
|
|
d1ba51bc84 | ||
|
|
9d73b2339a | ||
|
|
e88c96b1d7 | ||
|
|
3655e30adb | ||
|
|
112a400662 | ||
|
|
33a9603e93 | ||
|
|
ae6dfa9dae | ||
|
|
c05ae26ec8 | ||
|
|
3b2f29ef69 | ||
|
|
ecb61403d7 | ||
|
|
9eb37bff49 | ||
|
|
605109021f | ||
|
|
a3b7699f42 | ||
|
|
3251460ebd | ||
|
|
ec036ed185 | ||
|
|
5922ea1a0c | ||
|
|
1fb7d64171 | ||
|
|
fd8aa077e3 | ||
|
|
3e8a2aaddb | ||
|
|
c81c29ae58 | ||
|
|
5b4b6e7999 | ||
|
|
f565c92e38 | ||
|
|
fc4bf8158a | ||
|
|
7cd3aa7a1e | ||
|
|
ab4057880e | ||
|
|
d72885b383 | ||
|
|
fd1476e3aa | ||
|
|
142e4ebb57 | ||
|
|
ca92b9104c | ||
|
|
a6179d03f3 | ||
|
|
8afaf0380f | ||
|
|
ca0c409b6f | ||
|
|
177796ff79 | ||
|
|
da3be881ba | ||
|
|
1a0ec77094 | ||
|
|
e226021c5e | ||
|
|
1942c6a035 | ||
|
|
c49166401a | ||
|
|
500c0003b2 | ||
|
|
0275d77fc0 | ||
|
|
67a4b12e27 | ||
|
|
d1c08f11d5 | ||
|
|
40c0867f63 | ||
|
|
7081a85ace |
1
.babelrc
1
.babelrc
@@ -4,6 +4,7 @@
|
||||
"plugins": [
|
||||
"fbjs-scripts/babel-6/dev-expression",
|
||||
"syntax-trailing-function-commas",
|
||||
"babel-plugin-transform-object-rest-spread",
|
||||
"transform-es2015-template-literals",
|
||||
"transform-es2015-literals",
|
||||
"transform-es2015-arrow-functions",
|
||||
|
||||
@@ -58,6 +58,7 @@ script:
|
||||
-F "react-dom-server.min=@build/react-dom-server.min.js" \
|
||||
-F "npm-react=@build/packages/react.tgz" \
|
||||
-F "npm-react-dom=@build/packages/react-dom.tgz" \
|
||||
-F "npm-react-native=@build/packages/react-native-renderer.tgz" \
|
||||
-F "commit=$TRAVIS_COMMIT" \
|
||||
-F "date=`git log --format='%ct' -1`" \
|
||||
-F "pull_request=$TRAVIS_PULL_REQUEST" \
|
||||
|
||||
119
CHANGELOG.md
119
CHANGELOG.md
@@ -1,3 +1,120 @@
|
||||
## 15.0.2 (April 29, 2016)
|
||||
|
||||
### React
|
||||
- Removed extraneous files from npm package. ([@gaearon](https://github.com/gaearon) in [#6388](https://github.com/facebook/react/pull/6388))
|
||||
- Ensure `componentWillUnmount` is only called once. ([@jimfb](https://github.com/jimfb) in [#6613](https://github.com/facebook/react/pull/6613))
|
||||
|
||||
### ReactDOM
|
||||
- Fixed bug resulting in disabled buttons responding to mouse events in IE. ([@nhunzaker](https://github.com/nhunzaker) in [#6215](https://github.com/facebook/react/pull/6215))
|
||||
- Ensure `<option>`s are correctly selected when inside `<optgroup>`. ([@trevorsmith](https://github.com/trevorsmith) in [#6442](https://github.com/facebook/react/pull/6442))
|
||||
- Restore support for rendering into a shadow root. ([@Wildhoney](https://github.com/Wildhoney) in [#6462](https://github.com/facebook/react/pull/6462))
|
||||
- Ensure nested `<body>` elements are caught when warning for invalid markup. ([@keyanzhang](https://github.com/keyanzhang) in [#6469](https://github.com/facebook/react/pull/6469))
|
||||
- Improve warning when encountering multiple elements with the same key. ([@hkal](https://github.com/hkal) in [#6500](https://github.com/facebook/react/pull/6500))
|
||||
|
||||
### React TestUtils Add-on
|
||||
- Ensure that functional components do not have an owner. ([@gaearon](https://github.com/gaearon) in [#6362](https://github.com/facebook/react/pull/6362))
|
||||
- Handle invalid arguments to `scryRenderedDOMComponentsWithClass` better. ([@ipeters90](https://github.com/ipeters90) in [#6529](https://github.com/facebook/react/pull/6529))
|
||||
|
||||
### React Perf Add-on
|
||||
- Ignore DOM operations that occur outside the batch operation. ([@gaearon](https://github.com/gaearon) in [#6516](https://github.com/facebook/react/pull/6516))
|
||||
|
||||
### React Native Renderer
|
||||
- These files are now shipped inside the React npm package. They have no impact on React core or ReactDOM.
|
||||
|
||||
|
||||
## 15.0.1 (April 8, 2016)
|
||||
|
||||
### React
|
||||
- Restore `React.__spread` API to unbreak code compiled with some tools making use of this undocumented API. It is now officially deprecated. ([@zpao](https://github.com/zpao) in [#6444](https://github.com/facebook/react/pull/6444))
|
||||
|
||||
### ReactDOM
|
||||
- Fixed issue resulting in loss of cursor position in controlled inputs. ([@spicyj](https://github.com/spicyj) in [#6449](https://github.com/facebook/react/pull/6449))
|
||||
|
||||
|
||||
## 15.0.0 (April 7, 2016)
|
||||
|
||||
### Major changes
|
||||
|
||||
- **Initial render now uses `document.createElement` instead of generating HTML.** Previously we would generate a large string of HTML and then set `node.innerHTML`. At the time, this was decided to be faster than using `document.createElement` for the majority of cases and browsers that we supported. Browsers have continued to improve and so overwhelmingly this is no longer true. By using `createElement` we can make other parts of React faster. ([@spicyj](https://github.com/spicyj) in [#5205](https://github.com/facebook/react/pull/5205))
|
||||
- **`data-reactid` is no longer on every node.** As a result of using `document.createElement`, we can prime the node cache as we create DOM nodes, allowing us to skip a potential lookup (which used the `data-reactid` attribute). Root nodes will have a `data-reactroot` attribute and server generated markup will still contain `data-reactid`. ([@spicyj](https://github.com/spicyj) in [#5205](https://github.com/facebook/react/pull/5205))
|
||||
- **No more extra `<span>`s.** ReactDOM will now render plain text nodes interspersed with comment nodes that are used for demarcation. This gives us the same ability to update individual pieces of text, without creating extra nested nodes. If you were targeting these `<span>`s in your CSS, you will need to adjust accordingly. You can always render them explicitly in your components. ([@mwiencek](https://github.com/mwiencek) in [#5753](https://github.com/facebook/react/pull/5753))
|
||||
- **Rendering `null` now uses comment nodes.** Previously `null` would render to `<noscript>` elements. We now use comment nodes. This may cause issues if making use of `:nth-child` CSS selectors. While we consider this rendering behavior an implementation detail of React, it's worth noting the potential problem. ()[@spicyj](https://github.com/spicyj) in [#5451](https://github.com/facebook/react/pull/5451))
|
||||
- **Functional components can now return `null`.** We added support for [defining stateless components as functions](/react/blog/2015/09/10/react-v0.14-rc1.html#stateless-function-components) in React 0.14. However, React 0.14 still allowed you to define a class component without extending `React.Component` or using `React.createClass()`, so [we couldn’t reliably tell if your component is a function or a class](https://github.com/facebook/react/issues/5355), and did not allow returning `null` from it. This issue is solved in React 15, and you can now return `null` from any component, whether it is a class or a function. ([@jimfb](https://github.com/jimfb) in [#5884](https://github.com/facebook/react/pull/5884))
|
||||
- **Improved SVG support.** All SVG tags are now fully supported. (Uncommon SVG tags are not present on the `React.DOM` element helper, but JSX and `React.createElement` work on all tag names.) All SVG attributes that are implemented by the browsers should be supported too. If you find any attributes that we have missed, please [let us know in this issue](https://github.com/facebook/react/issues/1657). ([@zpao](https://github.com/zpao) in [#6243](https://github.com/facebook/react/pull/6243))
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- **No more extra `<span>`s.**
|
||||
- **`React.cloneElement()` now resolves `defaultProps`.** We fixed a bug in `React.cloneElement()` that some components may rely on. If some of the `props` received by `cloneElement()` are `undefined`, it used to return an element with `undefined` values for those props. We’re changing it to be consistent with `createElement()`. Now any `undefined` props passed to `cloneElement()` are resolved to the corresponding component’s `defaultProps`. ([@truongduy134](https://github.com/truongduy134) in [#5997](https://github.com/facebook/react/pull/5997))
|
||||
- **`ReactPerf.getLastMeasurements()` is opaque.** This change won’t affect applications but may break some third-party tools. We are [revamping `ReactPerf` implementation](https://github.com/facebook/react/pull/6046) and plan to release it during the 15.x cycle. The internal performance measurement format is subject to change so, for the time being, we consider the return value of `ReactPerf.getLastMeasurements()` an opaque data structure that should not be relied upon. ([@gaearon](https://github.com/gaearon) in [#6286](https://github.com/facebook/react/pull/6286))
|
||||
|
||||
#### Removed deprecations
|
||||
|
||||
These deprecations were introduced nine months ago in v0.14 with a warning and are removed:
|
||||
|
||||
- Deprecated APIs are removed from the `React` top-level export: `findDOMNode`, `render`, `renderToString`, `renderToStaticMarkup`, and `unmountComponentAtNode`. As a reminder, they are now available on `ReactDOM` and `ReactDOMServer`. ([@jimfb](https://github.com/jimfb) in [#5832](https://github.com/facebook/react/pull/5832))
|
||||
- Deprecated addons are removed: `batchedUpdates` and `cloneWithProps`. ([@jimfb](https://github.com/jimfb) in [#5859](https://github.com/facebook/react/pull/5859), [@zpao](https://github.com/zpao) in [#6016](https://github.com/facebook/react/pull/6016))
|
||||
- Deprecated component instance methods are removed: `setProps`, `replaceProps`, and `getDOMNode`. ([@jimfb](https://github.com/jimfb) in [#5570](https://github.com/facebook/react/pull/5570))
|
||||
- Deprecated CommonJS `react/addons` entry point is removed. As a reminder, you should use separate `react-addons-*` packages instead. This only applies if you use the CommonJS builds. ([@gaearon](https://github.com/gaearon) in [#6285](https://github.com/facebook/react/pull/6285))
|
||||
- Passing `children` to void elements like `<input>` was deprecated, and now throws an error. ([@jonhester](https://github.com/jonhester) in [#3372](https://github.com/facebook/react/pull/3372))
|
||||
- React-specific properties on DOM `refs` (e.g. `this.refs.div.props`) were deprecated, and are removed now. ([@jimfb](https://github.com/jimfb) in [#5495](https://github.com/facebook/react/pull/5495))
|
||||
|
||||
### New deprecations, introduced with a warning
|
||||
|
||||
Each of these changes will continue to work as before with a new warning until the release of React 16 so you can upgrade your code gradually.
|
||||
|
||||
- `LinkedStateMixin` and `valueLink` are now deprecated due to very low popularity. If you need this, you can use a wrapper component that implements the same behavior: [react-linked-input](https://www.npmjs.com/package/react-linked-input). ([@jimfb](https://github.com/jimfb) in [#6127](https://github.com/facebook/react/pull/6127))
|
||||
- Future versions of React will treat `<input value={null}>` as a request to clear the input. However, React 0.14 has been ignoring `value={null}`. React 15 warns you on a `null` input value and offers you to clarify your intention. To fix the warning, you may explicitly pass an empty string to clear a controlled input, or pass `undefined` to make the input uncontrolled. ([@antoaravinth](https://github.com/antoaravinth) in [#5048](https://github.com/facebook/react/pull/5048))
|
||||
- `ReactPerf.printDOM()` was renamed to `ReactPerf.printOperations()`, and `ReactPerf.getMeasurementsSummaryMap()` was renamed to `ReactPerf.getWasted()`. ([@gaearon](https://github.com/gaearon) in [#6287](https://github.com/facebook/react/pull/6287))
|
||||
|
||||
### New helpful warnings
|
||||
|
||||
- If you use a minified copy of the _development_ build, React DOM kindly encourages you to use the faster production build instead. ([@spicyj](https://github.com/spicyj) in [#5083](https://github.com/facebook/react/pull/5083))
|
||||
- React DOM: When specifying a unit-less CSS value as a string, a future version will not add `px` automatically. This version now warns in this case (ex: writing `style={{'{{'}}width: '300'}}`. Unitless *number* values like `width: 300` are unchanged. ([@pluma](https://github.com/pluma) in [#5140](https://github.com/facebook/react/pull/5140))
|
||||
- Synthetic Events will now warn when setting and accessing properties (which will not get cleared appropriately), as well as warn on access after an event has been returned to the pool. ([@kentcdodds](https://github.com/kentcdodds) in [#5940](https://github.com/facebook/react/pull/5940) and [@koba04](https://github.com/koba04) in [#5947](https://github.com/facebook/react/pull/5947))
|
||||
- Elements will now warn when attempting to read `ref` and `key` from the props. ([@prometheansacrifice](https://github.com/prometheansacrifice) in [#5744](https://github.com/facebook/react/pull/5744))
|
||||
- React will now warn if you pass a different `props` object to `super()` in the constructor. ([@prometheansacrifice](https://github.com/prometheansacrifice) in [#5346](https://github.com/facebook/react/pull/5346))
|
||||
- React will now warn if you call `setState()` inside `getChildContext()`. ([@raineroviir](https://github.com/raineroviir) in [#6121](https://github.com/facebook/react/pull/6121))
|
||||
- React DOM now attempts to warn for mistyped event handlers on DOM elements, such as `onclick` which should be `onClick`. ([@ali](https://github.com/ali) in [#5361](https://github.com/facebook/react/pull/5361))
|
||||
- React DOM now warns about `NaN` values in `style`. ([@jontewks](https://github.com/jontewks) in [#5811](https://github.com/facebook/react/pull/5811))
|
||||
- React DOM now warns if you specify both `value` and `defaultValue` for an input. ([@mgmcdermott](https://github.com/mgmcdermott) in [#5823](https://github.com/facebook/react/pull/5823))
|
||||
- React DOM now warns if an input switches between being controlled and uncontrolled. ([@TheBlasfem](https://github.com/TheBlasfem) in [#5864](https://github.com/facebook/react/pull/5864))
|
||||
- React DOM now warns if you specify `onFocusIn` or `onFocusOut` handlers as they are unnecessary in React. ([@jontewks](https://github.com/jontewks) in [#6296](https://github.com/facebook/react/pull/6296))
|
||||
- React now prints a descriptive error message when you pass an invalid callback as the last argument to `ReactDOM.render()`, `this.setState()`, or `this.forceUpdate()`. ([@conorhastings](https://github.com/conorhastings) in [#5193](https://github.com/facebook/react/pull/5193) and [@gaearon](https://github.com/gaearon) in [#6310](https://github.com/facebook/react/pull/6310))
|
||||
- Add-Ons: `TestUtils.Simulate()` now prints a helpful message if you attempt to use it with shallow rendering. ([@conorhastings](https://github.com/conorhastings) in [#5358](https://github.com/facebook/react/pull/5358))
|
||||
- PropTypes: `arrayOf()` and `objectOf()` provide better error messages for invalid arguments. ([@chicoxyzzy](https://github.com/chicoxyzzy) in [#5390](https://github.com/facebook/react/pull/5390))
|
||||
|
||||
### Notable bug fixes
|
||||
|
||||
- Fixed multiple small memory leaks. ([@spicyj](https://github.com/spicyj) in [#4983](https://github.com/facebook/react/pull/4983) and [@victor-homyakov](https://github.com/victor-homyakov) in [#6309](https://github.com/facebook/react/pull/6309))
|
||||
- Input events are handled more reliably in IE 10 and IE 11; spurious events no longer fire when using a placeholder. ([@jquense](https://github.com/jquense) in [#4051](https://github.com/facebook/react/pull/4051))
|
||||
- The `componentWillReceiveProps()` lifecycle method is now consistently called when `context` changes. ([@milesj](https://github.com/milesj) in [#5787](https://github.com/facebook/react/pull/5787))
|
||||
- `React.cloneElement()` doesn’t append slash to an existing `key` when used inside `React.Children.map()`. ([@ianobermiller](https://github.com/ianobermiller) in [#5892](https://github.com/facebook/react/pull/5892))
|
||||
- React DOM now supports the `cite` and `profile` HTML attributes. ([@AprilArcus](https://github.com/AprilArcus) in [#6094](https://github.com/facebook/react/pull/6094) and [@saiichihashimoto](https://github.com/saiichihashimoto) in [#6032](https://github.com/facebook/react/pull/6032))
|
||||
- React DOM now supports `cssFloat`, `gridRow` and `gridColumn` CSS properties. ([@stevenvachon](https://github.com/stevenvachon) in [#6133](https://github.com/facebook/react/pull/6133) and [@mnordick](https://github.com/mnordick) in [#4779](https://github.com/facebook/react/pull/4779))
|
||||
- React DOM now correctly handles `borderImageOutset`, `borderImageWidth`, `borderImageSlice`, `floodOpacity`, `strokeDasharray`, and `strokeMiterlimit` as unitless CSS properties. ([@rofrischmann](https://github.com/rofrischmann) in [#6210](https://github.com/facebook/react/pull/6210) and [#6270](https://github.com/facebook/react/pull/6270))
|
||||
- React DOM now supports the `onAnimationStart`, `onAnimationEnd`, `onAnimationIteration`, `onTransitionEnd`, and `onInvalid` events. Support for `onLoad` has been added to `object` elements. ([@tomduncalf](https://github.com/tomduncalf) in [#5187](https://github.com/facebook/react/pull/5187), [@milesj](https://github.com/milesj) in [#6005](https://github.com/facebook/react/pull/6005), and [@ara4n](https://github.com/ara4n) in [#5781](https://github.com/facebook/react/pull/5781))
|
||||
- React DOM now defaults to using DOM attributes instead of properties, which fixes a few edge case bugs. Additionally the nullification of values (ex: `href={null}`) now results in the forceful removal, no longer trying to set to the default value used by browsers in the absence of a value. ([@syranide](https://github.com/syranide) in [#1510](https://github.com/facebook/react/pull/1510))
|
||||
- React DOM does not mistakingly coerce `children` to strings for Web Components. ([@jimfb](https://github.com/jimfb) in [#5093](https://github.com/facebook/react/pull/5093))
|
||||
- React DOM now correctly normalizes SVG `<use>` events. ([@edmellum](https://github.com/edmellum) in [#5720](https://github.com/facebook/react/pull/5720))
|
||||
- React DOM does not throw if a `<select>` is unmounted while its `onChange` handler is executing. ([@sambev](https://github.com/sambev) in [#6028](https://github.com/facebook/react/pull/6028))
|
||||
- React DOM does not throw in Windows 8 apps. ([@Andrew8xx8](https://github.com/Andrew8xx8) in [#6063](https://github.com/facebook/react/pull/6063))
|
||||
- React DOM does not throw when asynchronously unmounting a child with a `ref`. ([@yiminghe](https://github.com/yiminghe) in [#6095](https://github.com/facebook/react/pull/6095))
|
||||
- React DOM no longer forces synchronous layout because of scroll position tracking. ([@syranide](https://github.com/syranide) in [#2271](https://github.com/facebook/react/pull/2271))
|
||||
- `Object.is` is used in a number of places to compare values, which leads to fewer false positives, especially involving `NaN`. In particular, this affects the `shallowCompare` add-on. ([@chicoxyzzy](https://github.com/chicoxyzzy) in [#6132](https://github.com/facebook/react/pull/6132))
|
||||
- Add-Ons: ReactPerf no longer instruments adding or removing an event listener because they don’t really touch the DOM due to event delegation. ([@antoaravinth](https://github.com/antoaravinth) in [#5209](https://github.com/facebook/react/pull/5209))
|
||||
|
||||
### Other improvements
|
||||
|
||||
- React now uses `loose-envify` instead of `envify` so it installs less transitive dependencies. ([@qerub](https://github.com/qerub) in [#6303](https://github.com/facebook/react/pull/6303))
|
||||
- Shallow renderer now exposes `getMountedInstance()`. ([@glenjamin](https://github.com/glenjamin) in [#4918](https://github.com/facebook/react/pull/4918))
|
||||
- Shallow renderer now returns the rendered output from `render()`. ([@simonewebdesign](https://github.com/simonewebdesign) in [#5411](https://github.com/facebook/react/pull/5411))
|
||||
- React no longer depends on ES5 *shams* for `Object.create` and `Object.freeze` in older environments. It still, however, requires ES5 *shims* in those environments. ([@dgreensp](https://github.com/dgreensp) in [#4959](https://github.com/facebook/react/pull/4959))
|
||||
- React DOM now allows `data-` attributes with names that start with numbers. ([@nLight](https://github.com/nLight) in [#5216](https://github.com/facebook/react/pull/5216))
|
||||
- React DOM adds a new `suppressContentEditableWarning` prop for components like [Draft.js](https://facebook.github.io/draft-js/) that intentionally manage `contentEditable` children with React. ([@mxstbr](https://github.com/mxstbr) in [#6112](https://github.com/facebook/react/pull/6112))
|
||||
- React improves the performance for `createClass()` on complex specs. ([@spicyj](https://github.com/spicyj) in [#5550](https://github.com/facebook/react/pull/5550))
|
||||
|
||||
|
||||
## 0.14.8 (March 29, 2016)
|
||||
|
||||
### React
|
||||
@@ -88,7 +205,7 @@
|
||||
### Major changes
|
||||
|
||||
- Split the main `react` package into two: `react` and `react-dom`. This paves the way to writing components that can be shared between the web version of React and React Native. This means you will need to include both files and some functions have been moved from `React` to `ReactDOM`.
|
||||
- Addons have been moved to seperate packages (`react-addons-clone-with-props`, `react-addons-create-fragment`, `react-addons-css-transition-group`, `react-addons-linked-state-mixin`, `react-addons-perf`, `react-addons-pure-render-mixin`, `react-addons-shallow-compare`, `react-addons-test-utils`, `react-addons-transition-group`, `react-addons-update`, `ReactDOM.unstable_batchedUpdates`).
|
||||
- Addons have been moved to separate packages (`react-addons-clone-with-props`, `react-addons-create-fragment`, `react-addons-css-transition-group`, `react-addons-linked-state-mixin`, `react-addons-perf`, `react-addons-pure-render-mixin`, `react-addons-shallow-compare`, `react-addons-test-utils`, `react-addons-transition-group`, `react-addons-update`, `ReactDOM.unstable_batchedUpdates`).
|
||||
- Stateless functional components - React components were previously created using React.createClass or using ES6 classes. This release adds a [new syntax](https://facebook.github.io/react/docs/reusable-components.html#stateless-functions) where a user defines a single [stateless render function](https://facebook.github.io/react/docs/reusable-components.html#stateless-functions) (with one parameter: `props`) which returns a JSX element, and this function may be used as a component.
|
||||
- Refs to DOM components as the DOM node itself. Previously the only useful thing you can do with a DOM component is call `getDOMNode()` to get the underlying DOM node. Starting with this release, a ref to a DOM component _is_ the actual DOM node. **Note that refs to custom (user-defined) components work exactly as before; only the built-in DOM components are affected by this change.**
|
||||
|
||||
|
||||
@@ -74,6 +74,10 @@ module.exports = function(grunt) {
|
||||
grunt.registerTask('npm-react-dom:release', npmReactDOMTasks.buildRelease);
|
||||
grunt.registerTask('npm-react-dom:pack', npmReactDOMTasks.packRelease);
|
||||
|
||||
var npmReactNativeTasks = require('./grunt/tasks/npm-react-native');
|
||||
grunt.registerTask('npm-react-native:release', npmReactNativeTasks.buildRelease);
|
||||
grunt.registerTask('npm-react-native:pack', npmReactNativeTasks.packRelease);
|
||||
|
||||
var npmReactAddonsTasks = require('./grunt/tasks/npm-react-addons');
|
||||
grunt.registerTask('npm-react-addons:release', npmReactAddonsTasks.buildReleases);
|
||||
grunt.registerTask('npm-react-addons:pack', npmReactAddonsTasks.packReleases);
|
||||
@@ -127,6 +131,8 @@ module.exports = function(grunt) {
|
||||
'npm-react:pack',
|
||||
'npm-react-dom:release',
|
||||
'npm-react-dom:pack',
|
||||
'npm-react-native:release',
|
||||
'npm-react-native:pack',
|
||||
'npm-react-addons:release',
|
||||
'npm-react-addons:pack',
|
||||
'compare_size',
|
||||
|
||||
@@ -37,12 +37,12 @@ The fastest way to get started is to serve JavaScript from the CDN (also availab
|
||||
|
||||
```html
|
||||
<!-- The core React library -->
|
||||
<script src="https://fb.me/react-0.14.8.js"></script>
|
||||
<script src="https://fb.me/react-15.0.2.js"></script>
|
||||
<!-- The ReactDOM Library -->
|
||||
<script src="https://fb.me/react-dom-0.14.8.js"></script>
|
||||
<script src="https://fb.me/react-dom-15.0.2.js"></script>
|
||||
```
|
||||
|
||||
We've also built a [starter kit](https://facebook.github.io/react/downloads/react-0.14.8.zip) which might be useful if this is your first time using React. It includes a webpage with an example of using React with live code.
|
||||
We've also built a [starter kit](https://facebook.github.io/react/downloads/react-15.0.2.zip) which might be useful if this is your first time using React. It includes a webpage with an example of using React with live code.
|
||||
|
||||
If you'd like to use [bower](http://bower.io), it's as easy as:
|
||||
|
||||
|
||||
@@ -36,13 +36,13 @@ sass:
|
||||
sass_dir: _css
|
||||
gems:
|
||||
- jekyll-redirect-from
|
||||
react_version: 0.14.7
|
||||
react_version: 15.0.1
|
||||
react_hashes:
|
||||
dev: xQae1pUPdAKUe0u0KUTNt09zzdwheX4VSUsV8vatqM+t6X7rta01qOzessL808ox
|
||||
prod: zTm/dblzLXQNp3CgY+hfaC/WJ6h4XtNrePh2CW2+rO9GPuNiPb9jmthvAL+oI/dQ
|
||||
addons_dev: I5TF2q2QDmB31aN5lcClArdUo+WJH/Yi3hcH3PBVXFe5DYtYCFh7Jx/dmpba12zn
|
||||
addons_prod: KPHTQfiYMhtsIRbZcY4ri1lBYZQbj4ePsSdzODR2Bu5L5ts3APVyqwKPBThO5Hgc
|
||||
dom_dev: A1t0GCrR06cTHvMjaxeSE8XOiz6j7NvWdmxhN/9z748wEvJTVk13Rr8gMzTUnd8G
|
||||
dom_prod: ntqCsHbLdMxT352UbhPbT7fqjE8xi4jLmQYQa8mYR+ylAapbXRfdsDweueDObf7m
|
||||
dom_server_dev: 3I5+eGB/ILYa6pQQX+rM9O0SyDltamM40RiZ5JvIijSYEfVGZU0vY4Iwx9a1eYyD
|
||||
dom_server_prod: Kt9dEqXzv00orFPW2o3H+kxQtSiNO8EqXsXJT3i99rCcp74N/Km98V0kUxAzy44k
|
||||
dev: PcrCc8vAh3jounTGQGibvUE4liSu8s91ycPotf/cfEHdSnq9qaW2c9tFr3oyDzW4
|
||||
prod: S9sTQnq5vvBUhj91S9s9dhE4fiZWgytnfFyIW7hu3Vk5leHzq03A9dULSd0IqW9N
|
||||
addons_dev: TUMXymomVw+mFbSSojSWXtBVJiWEfUKUYzgZsgvUsdD9fL/y0PPc55825Ui2ZPY6
|
||||
addons_prod: rBjtXXl33XbsJR2IbEYJRYH5VnUUMi4o6Zr7ai4UPxcrY0Cw3QpnaU9iIJ2GcyLy
|
||||
dom_dev: ukADzi8g85qHW1NDinecbLJu/a+yfhgXK6G2Kl7vCckNICr4Pt1hN7mpfPzsgbD2
|
||||
dom_prod: n1Sg+vSen3Mb4Ggzsi2jq4MHCXmlKD0spQXmiYaPSt/8WR7PzpwLTmH8OI+yAluA
|
||||
dom_server_dev: eQOoGEpIR1fqOenVhLKRmPctitl54hZLIFg9uMYElEMx0r175Qedt3utJwCjsSrr
|
||||
dom_server_prod: 41Tbqk6dOco41BSH7Fb2xAWh8PJTYntH+OR/m80NT+L5IF6XWcq/kvNZGzhBws60
|
||||
|
||||
@@ -2,17 +2,21 @@
|
||||
- - '839'
|
||||
- Aaron Franks
|
||||
- Aaron Gelter
|
||||
- Adam Bloomston
|
||||
- Adam Krebs
|
||||
- Adam Mark
|
||||
- Adam Solove
|
||||
- Adam Timberlake
|
||||
- Adam Zapletal
|
||||
- Ahmad Wali Sidiqi
|
||||
- Alan Plum
|
||||
- Alan Souza
|
||||
- Alan deLevie
|
||||
- Alastair Hole
|
||||
- Alex
|
||||
- Alex Boatwright
|
||||
- Alex Boyd
|
||||
- Alex Dajani
|
||||
- Alex Lopatin
|
||||
- Alex Mykyta
|
||||
- Alex Pien
|
||||
@@ -24,22 +28,28 @@
|
||||
- Alexandre Gaudencio
|
||||
- Alexey Raspopov
|
||||
- Alexey Shamrin
|
||||
- Ali Ukani
|
||||
- Andre Z Sanchez
|
||||
- Andreas Savvides
|
||||
- Andreas Svensson
|
||||
- Andres Kalle
|
||||
- Andres Suarez
|
||||
- Andrew Clark
|
||||
- Andrew Cobby
|
||||
- Andrew Davey
|
||||
- Andrew Henderson
|
||||
- Andrew Kulakov
|
||||
- Andrew Rasmussen
|
||||
- Andrew Sokolov
|
||||
- Andrew Zich
|
||||
- Andrey Popp
|
||||
- Anthony van der Hoorn
|
||||
- Anto Aravinth
|
||||
- Antonio Ruberto
|
||||
- Antti Ahti
|
||||
- Anuj Tomar
|
||||
- AoDev
|
||||
- April Arcus
|
||||
- Areeb Malik
|
||||
- Aria Buckles
|
||||
- Aria Stewart
|
||||
@@ -54,7 +64,9 @@
|
||||
- Beau Smith
|
||||
- Ben Alpert
|
||||
- Ben Anderson
|
||||
- Ben Brooks
|
||||
- Ben Foxall
|
||||
- Ben Halpern
|
||||
- Ben Jaffe
|
||||
- Ben Moss
|
||||
- Ben Newman
|
||||
@@ -62,16 +74,20 @@
|
||||
- Benjamin Keen
|
||||
- Benjamin Leiken
|
||||
- Benjamin Woodruff
|
||||
- Benjy Cui
|
||||
- Bill Blanchard
|
||||
- Bill Fisher
|
||||
- Blaine Hatab
|
||||
- Blaine Kasten
|
||||
- Bob Eagan
|
||||
- Bob Ralian
|
||||
- Bob Renwick
|
||||
- Bobby
|
||||
- Bojan Mihelac
|
||||
- Bradley Spaulding
|
||||
- Brandon Bloom
|
||||
- Brandon Tilley
|
||||
- Brenard Cubacub
|
||||
- Brian Cooke
|
||||
- Brian Holt
|
||||
- Brian Hsu
|
||||
@@ -82,14 +98,19 @@
|
||||
- Bruno Škvorc
|
||||
- Cam Song
|
||||
- Cam Spiers
|
||||
- Cameron Chamberlain
|
||||
- Cameron Matheson
|
||||
- Carter Chung
|
||||
- Cassus Adam Banko
|
||||
- Cat Chen
|
||||
- Cedric Sohrauer
|
||||
- Cesar William Alvarenga
|
||||
- Changsoon Bok
|
||||
- Charles Marsh
|
||||
- Chase Adams
|
||||
- Cheng Lou
|
||||
- Chitharanjan Das
|
||||
- Chris Bolin
|
||||
- Chris Grovers
|
||||
- Chris Ha
|
||||
- Chris Rebert
|
||||
@@ -97,12 +118,15 @@
|
||||
- Christian Alfoni
|
||||
- Christian Oliff
|
||||
- Christian Roman
|
||||
- Christoffer Sawicki
|
||||
- Christoph Pojer
|
||||
- Christopher Monsanto
|
||||
- Clay Allsopp
|
||||
- Connor McSheffrey
|
||||
- Conor Hastings
|
||||
- Cory House
|
||||
- Cotton Hou
|
||||
- Craig Akimoto
|
||||
- Cristovao Verstraeten
|
||||
- Damien Pellier
|
||||
- Dan Abramov
|
||||
@@ -113,6 +137,7 @@
|
||||
- Daniel Friesen
|
||||
- Daniel Gasienica
|
||||
- Daniel Hejl
|
||||
- Daniel Hejl
|
||||
- Daniel Lo Nigro
|
||||
- Daniel Mané
|
||||
- Daniel Miladinov
|
||||
@@ -122,23 +147,31 @@
|
||||
- Darcy
|
||||
- Daryl Lau
|
||||
- Darío Javier Cravero
|
||||
- Dave Galbraith
|
||||
- David Baker
|
||||
- David Ed Mellum
|
||||
- David Goldberg
|
||||
- David Granado
|
||||
- David Greenspan
|
||||
- David Hellsing
|
||||
- David Hu
|
||||
- David Khourshid
|
||||
- David Mininger
|
||||
- David Neubauer
|
||||
- David Percy
|
||||
- Dean Shi
|
||||
- Denis Sokolov
|
||||
- Deniss Jacenko
|
||||
- Dennis Johnson
|
||||
- Devon Blandin
|
||||
- Devon Harvey
|
||||
- Dmitrii Abramov
|
||||
- Dmitriy Rozhkov
|
||||
- Dmitry Blues
|
||||
- Dmitry Mazuro
|
||||
- Domenico Matteo
|
||||
- Don Abrams
|
||||
- Dongsheng Liu
|
||||
- Dustan Kasten
|
||||
- Dustin Getz
|
||||
- Dylan Harrington
|
||||
@@ -154,6 +187,7 @@
|
||||
- Erik Harper
|
||||
- Espen Hovlandsdal
|
||||
- Evan Coonrod
|
||||
- Evan Vosberg
|
||||
- Fabio M. Costa
|
||||
- Federico Rampazzo
|
||||
- Felipe Oliveira Carvalho
|
||||
@@ -163,11 +197,15 @@
|
||||
- Frankie Bagnardi
|
||||
- François-Xavier Bois
|
||||
- Fred Zhao
|
||||
- Freddy Rangel
|
||||
- Fyodor Ivanishchev
|
||||
- G Scott Olson
|
||||
- G. Kay Lee
|
||||
- Gabe Levi
|
||||
- Gajus Kuizinas
|
||||
- Gareth Nicholson
|
||||
- Garren Smith
|
||||
- Gavin McQuistin
|
||||
- Geert Pasteels
|
||||
- Geert-Jan Brits
|
||||
- George A Sisco III
|
||||
@@ -183,44 +221,57 @@
|
||||
- Guido Bouman
|
||||
- Harry Hull
|
||||
- Harry Marr
|
||||
- - Harry Moreno
|
||||
- Harry Moreno
|
||||
- Harshad Sabne
|
||||
- Hekar Khani
|
||||
- Hendrik Swanepoel
|
||||
- Henrik Nyh
|
||||
- Henry Wong
|
||||
- Henry Zhu
|
||||
- Hideo Matsumoto
|
||||
- Hou Chia
|
||||
- Huang-Wei Chang
|
||||
- - Hugo Agbonon
|
||||
- Hugo Jobling
|
||||
- Hyeock Kwon
|
||||
- Héliton Nordt
|
||||
- Ian Obermiller
|
||||
- Ignacio Carbajo
|
||||
- Igor Scekic
|
||||
- Ilia Pavlenkov
|
||||
- Ilya Shuklin
|
||||
- Ilyá Belsky
|
||||
- Ingvar Stepanyan
|
||||
- Irae Carvalho
|
||||
- Isaac Salier-Hellendag
|
||||
- Iurii Kucherov
|
||||
- Ivan Kozik
|
||||
- Ivan Krechetov
|
||||
- Ivan Vergiliev
|
||||
- J. Andrew Brassington
|
||||
- J. Renée Beach
|
||||
- JD Isaacks
|
||||
- JJ Weber
|
||||
- JW
|
||||
- Jack Zhang
|
||||
- Jackie Wung
|
||||
- Jacob Gable
|
||||
- Jacob Greenleaf
|
||||
- Jae Hun Ro
|
||||
- Jaeho Lee
|
||||
- Jaime Mingo
|
||||
- Jake Worth
|
||||
- Jakub Malinowski
|
||||
- James
|
||||
- James Brantly
|
||||
- James Burnett
|
||||
- James Friend
|
||||
- James Ide
|
||||
- James Long
|
||||
- James Pearce
|
||||
- James Seppi
|
||||
- James South
|
||||
- James Wen
|
||||
- Jamie Wong
|
||||
- Jamis Charles
|
||||
- Jamison Dance
|
||||
@@ -246,6 +297,7 @@
|
||||
- Jeff Morrison
|
||||
- Jeff Welch
|
||||
- Jeffrey Lin
|
||||
- Jeremy Fairbank
|
||||
- Jesse Skinner
|
||||
- Jignesh Kakadiya
|
||||
- Jim OBrien
|
||||
@@ -253,14 +305,19 @@
|
||||
- Jimmy Jea
|
||||
- Jing Chen
|
||||
- Jinwoo Oh
|
||||
- Jinxiu Lee
|
||||
- Jiyeon Seo
|
||||
- Jody McIntyre
|
||||
- Joe Critchley
|
||||
- Joe Stein
|
||||
- Joel Auterson
|
||||
- Johannes Baiter
|
||||
- Johannes Emerich
|
||||
- Johannes Lumpe
|
||||
- John Heroy
|
||||
- John Ryan
|
||||
- John Watson
|
||||
- John-David Dalton
|
||||
- Jon Beebe
|
||||
- Jon Chester
|
||||
- Jon Hester
|
||||
@@ -270,6 +327,7 @@
|
||||
- Jonas Enlund
|
||||
- Jonas Gebhardt
|
||||
- Jonathan Hsu
|
||||
- Jonathan Persson
|
||||
- Jordan Harband
|
||||
- Jordan Walke
|
||||
- Jorrit Schippers
|
||||
@@ -277,7 +335,9 @@
|
||||
- Joseph Savona
|
||||
- Josh Bassett
|
||||
- Josh Duck
|
||||
- Josh Perez
|
||||
- Josh Yudaken
|
||||
- Joshua Evans
|
||||
- Joshua Go
|
||||
- Joshua Goldberg
|
||||
- Joshua Ma
|
||||
@@ -286,23 +346,34 @@
|
||||
- Julen Ruiz Aizpuru
|
||||
- Julian Viereck
|
||||
- Julien Bordellier
|
||||
- Julio Lopez
|
||||
- Jun Wu
|
||||
- Juraj Dudak
|
||||
- Justas Brazauskas
|
||||
- Justin Jaffray
|
||||
- Justin Robison
|
||||
- Justin Woo
|
||||
- Kale
|
||||
- Kamron Batman
|
||||
- Karl Mikkelsen
|
||||
- Karpich Dmitry
|
||||
- Keito Uchiyama
|
||||
- Ken Powers
|
||||
- Kent C. Dodds
|
||||
- Kevin Cheng
|
||||
- Kevin Coughlin
|
||||
- Kevin Huang
|
||||
- Kevin Lau
|
||||
- Kevin Old
|
||||
- Kevin Robinson
|
||||
- Kewei Jiang
|
||||
- Kier Borromeo
|
||||
- KimCoding
|
||||
- Kirk Steven Hansen
|
||||
- Kit Randel
|
||||
- Kohei TAKATA
|
||||
- Koo Youngmin
|
||||
- Krystian Karczewski
|
||||
- Kunal Mehta
|
||||
- Kurt Ruppel
|
||||
- Kyle Kelley
|
||||
@@ -312,6 +383,7 @@
|
||||
- Lee Byron
|
||||
- Lee Jaeyoung
|
||||
- Lei
|
||||
- Leland Richardson
|
||||
- Leon Fedotov
|
||||
- Leon Yip
|
||||
- Leonardo YongUk Kim
|
||||
@@ -319,9 +391,13 @@
|
||||
- Levi McCallum
|
||||
- Lily
|
||||
- Logan Allen
|
||||
- Lovisa Svallingson
|
||||
- Ludovico Fischer
|
||||
- Luigy Leon
|
||||
- Luke Horvat
|
||||
- MIKAMI Yoshiyuki
|
||||
- Maher Beg
|
||||
- Manas
|
||||
- Marcin K.
|
||||
- Marcin Kwiatkowski
|
||||
- Marcin Szczepanski
|
||||
@@ -331,59 +407,83 @@
|
||||
- Mark Funk
|
||||
- Mark Hintz
|
||||
- Mark IJbema
|
||||
- Mark Murphy
|
||||
- Mark Richardson
|
||||
- Mark Rushakoff
|
||||
- Mark Sun
|
||||
- Marlon Landaverde
|
||||
- Marshall Roch
|
||||
- Martin Andert
|
||||
- Martin Hujer
|
||||
- Martin Jul
|
||||
- Martin Konicek
|
||||
- Martin Mihaylov
|
||||
- Masaki KOBAYASHI
|
||||
- Mathieu M-Gosselin
|
||||
- Mathieu Savy
|
||||
- Matias Singers
|
||||
- Matsunoki
|
||||
- Matt Brookes
|
||||
- Matt Dunn-Rankin
|
||||
- Matt Harrison
|
||||
- Matt Huggins
|
||||
- Matt Stow
|
||||
- Matt Zabriskie
|
||||
- Matthew Dapena-Tretter
|
||||
- Matthew Herbst
|
||||
- Matthew Hodgson
|
||||
- Matthew Johnston
|
||||
- Matthew King
|
||||
- Matthew Looi
|
||||
- Matthew Miner
|
||||
- Matthias Le Brun
|
||||
- Matti Nelimarkka
|
||||
- Mattijs Kneppers
|
||||
- Max F. Albrecht
|
||||
- Max Heiber
|
||||
- Max Stoiber
|
||||
- Maxi Ferreira
|
||||
- Maxim Abramchuk
|
||||
- Merrick Christensen
|
||||
- Mert Kahyaoğlu
|
||||
- Michael Chan
|
||||
- Michael McDermott
|
||||
- Michael Randers-Pehrson
|
||||
- Michael Ridgway
|
||||
- Michael Warner
|
||||
- Michael Wiencek
|
||||
- Michael Ziwisky
|
||||
- Michal Srb
|
||||
- Michelle Todd
|
||||
- Mihai Parparita
|
||||
- Mike D Pilsbury
|
||||
- - Mike Groseclose
|
||||
- Mike Groseclose
|
||||
- Mike Nordick
|
||||
- Mikolaj Dadela
|
||||
- Miles Johnson
|
||||
- Minwe LUO
|
||||
- Miorel Palii
|
||||
- Morhaus
|
||||
- - Morhaus
|
||||
- Moshe Kolodny
|
||||
- Mouad Debbar
|
||||
- Murad
|
||||
- Murray M. Moss
|
||||
- Nadeesha Cabral
|
||||
- Naman Goel
|
||||
- Nate Hunzaker
|
||||
- Nate Lee
|
||||
- Nathan Smith
|
||||
- Nathan White
|
||||
- Nee
|
||||
- Neri Marschik
|
||||
- Nguyen Truong Duy
|
||||
- Nicholas Bergson-Shilcock
|
||||
- Nicholas Clawson
|
||||
- Nick Balestra
|
||||
- Nick Fitzgerald
|
||||
- Nick Gavalas
|
||||
- Nick Merwin
|
||||
- Nick Presta
|
||||
- Nick Raienko
|
||||
- Nick Thompson
|
||||
- Nick Williams
|
||||
@@ -399,8 +499,10 @@
|
||||
- Pablo Lacerda de Miranda
|
||||
- Paolo Moretti
|
||||
- Pascal Hartig
|
||||
- Patrick
|
||||
- Patrick Laughlin
|
||||
- Patrick Stapleton
|
||||
- Paul Benigeri
|
||||
- Paul Harper
|
||||
- Paul O’Shannessy
|
||||
- Paul Seiffert
|
||||
@@ -410,19 +512,25 @@
|
||||
- Peter Blazejewicz
|
||||
- Peter Cottle
|
||||
- Peter Jaros
|
||||
- Peter Newnham
|
||||
- Petri Lehtinen
|
||||
- Petri Lievonen
|
||||
- Pieter Vanderwerff
|
||||
- Pouja Nikray
|
||||
- Prathamesh Sonpatki
|
||||
- Prayag Verma
|
||||
- Preston Parry
|
||||
- Rafael
|
||||
- Rafal Dittwald
|
||||
- Rainer Oviir
|
||||
- Rajat Sehgal
|
||||
- Rajiv Tirumalareddy
|
||||
- Ram Kaniyur
|
||||
- Randall Randall
|
||||
- Ray
|
||||
- Raymond Ha
|
||||
- Reed Loden
|
||||
- Remko Tronçon
|
||||
- Richard D. Worth
|
||||
- Richard Feldman
|
||||
- Richard Kho
|
||||
@@ -430,16 +538,22 @@
|
||||
- Richard Livesey
|
||||
- Richard Wood
|
||||
- Rick Beerendonk
|
||||
- Rick Ford
|
||||
- Riley Tomasek
|
||||
- Rob Arnold
|
||||
- Robert Binna
|
||||
- Robert Knight
|
||||
- Robert Sedovsek
|
||||
- Robin Berjon
|
||||
- Robin Frischmann
|
||||
- Roman Pominov
|
||||
- Roman Vanesyan
|
||||
- Russ
|
||||
- Ryan Seddon
|
||||
- Sahat Yalkabov
|
||||
- Saif Hakim
|
||||
- Saiichi Hashimoto
|
||||
- Sam Beveridge
|
||||
- Sam Saccone
|
||||
- Sam Selikoff
|
||||
- Samy Al Zahrani
|
||||
@@ -448,7 +562,9 @@
|
||||
- Scott Feeney
|
||||
- Sean Kinsey
|
||||
- Sebastian Markbåge
|
||||
- Sebastian McKenzie
|
||||
- Seoh Char
|
||||
- Sercan Eraslan
|
||||
- Serg
|
||||
- Sergey Generalov
|
||||
- Sergey Rubanov
|
||||
@@ -457,18 +573,25 @@
|
||||
- Shaun Trennery
|
||||
- ShihChi Huang
|
||||
- Shim Won
|
||||
- Shinnosuke Watanabe
|
||||
- Shogun Sea
|
||||
- Shota Kubota
|
||||
- Shripad K
|
||||
- Sibi
|
||||
- Simen Bekkhus
|
||||
- Simon Højberg
|
||||
- Simon Welsh
|
||||
- Simone Vittori
|
||||
- Soichiro Kawamura
|
||||
- Sophia Westwood
|
||||
- Sota Ohara
|
||||
- Spencer Handley
|
||||
- Stefan Dombrowski
|
||||
- Stephen Murphy
|
||||
- Sterling Cobb
|
||||
- Steve Baker
|
||||
- Steven Luscher
|
||||
- Steven Vachon
|
||||
- Stoyan Stefanov
|
||||
- Sundeep Malladi
|
||||
- Sunny Juneja
|
||||
@@ -479,9 +602,11 @@
|
||||
- Taeho Kim
|
||||
- Tay Yang Shun
|
||||
- Ted Kim
|
||||
- Tengfei Guo
|
||||
- Teodor Szente
|
||||
- Thomas Aylott
|
||||
- Thomas Boyt
|
||||
- Thomas Broadley
|
||||
- Thomas Reggi
|
||||
- Thomas Röggla
|
||||
- Thomas Shaddox
|
||||
@@ -491,12 +616,15 @@
|
||||
- Tim Routowicz
|
||||
- Tim Schaub
|
||||
- Timothy Yung
|
||||
- Timur Carpeev
|
||||
- Tobias Reiss
|
||||
- Tom Duncalf
|
||||
- Tom Haggie
|
||||
- Tom Hauburger
|
||||
- Tom MacWright
|
||||
- Tom Occhino
|
||||
- Tomasz Kołodziejski
|
||||
- Tomoya Suzuki
|
||||
- Tony Spiro
|
||||
- Toru Kobayashi
|
||||
- Trinh Hoang Nhu
|
||||
@@ -507,14 +635,17 @@
|
||||
- Varun Rau
|
||||
- Vasiliy Loginevskiy
|
||||
- Victor Alvarez
|
||||
- Victor Homyakov
|
||||
- Victor Koenders
|
||||
- Ville Immonen
|
||||
- Vincent Riemer
|
||||
- Vincent Siao
|
||||
- Vipul A M
|
||||
- Vitaly Kramskikh
|
||||
- Vitor Balocco
|
||||
- Vjeux
|
||||
- Volkan Unsal
|
||||
- Wander Wang
|
||||
- Wayne Larsen
|
||||
- WickyNilliams
|
||||
- Wincent Colaiuta
|
||||
@@ -526,9 +657,14 @@
|
||||
- YouBao Nong
|
||||
- Yuichi Hagio
|
||||
- Yuriy Dybskiy
|
||||
- Yutaka Nakajima
|
||||
- Yuval Dekel
|
||||
- Zach Bruggeman
|
||||
- Zach Ramaekers
|
||||
- Zacharias
|
||||
- Zeke Sikelianos
|
||||
- Zhangjd
|
||||
- adraeth
|
||||
- arush
|
||||
- brafdlog
|
||||
- chen
|
||||
@@ -539,14 +675,23 @@
|
||||
- dongmeng.ldm
|
||||
- iamchenxin
|
||||
- iamdoron
|
||||
- iawia002
|
||||
- imagentleman
|
||||
- koh-taka
|
||||
- kohashi85
|
||||
- laiso
|
||||
- leeyoungalias
|
||||
- li.li
|
||||
- maxprafferty
|
||||
- rgarifullin
|
||||
- songawee
|
||||
- sugarshin
|
||||
- wali-s
|
||||
- yiminghe
|
||||
- youmoo
|
||||
- zhangjg
|
||||
- Árni Hermann Reynisson
|
||||
- 凌恒
|
||||
- 张敏
|
||||
- zwhitchcox
|
||||
- "Árni Hermann Reynisson"
|
||||
- "元彦"
|
||||
- "凌恒"
|
||||
- "张敏"
|
||||
|
||||
@@ -48,6 +48,13 @@
|
||||
title: Refs to Components
|
||||
- id: tooling-integration
|
||||
title: Tooling Integration
|
||||
subitems:
|
||||
- id: language-tooling
|
||||
title: Language Tooling
|
||||
- id: package-management
|
||||
title: Package Management
|
||||
- id: environments
|
||||
title: Server-side Environments
|
||||
- id: addons
|
||||
title: Add-Ons
|
||||
subitems:
|
||||
|
||||
@@ -10,7 +10,7 @@ amazed that 600 people requested to be notified when ticket go on sale. This is
|
||||
When we organized the conference, we decided to start small since this is the
|
||||
first React.js conference. Also, we weren't sure what level of demand to expect,
|
||||
so we planned for a single-track, two-day conference on Facebook's campus. The
|
||||
largest room available would accomodate 18 speaking slots and 200 attendees.
|
||||
largest room available would accommodate 18 speaking slots and 200 attendees.
|
||||
The spacial configuration makes it difficult to add a second track and changing
|
||||
venues only two months in advance would be too difficult, so we are deciding to
|
||||
stick with the originally planned format and venue on Facebook's campus.
|
||||
|
||||
259
docs/_posts/2016-04-07-react-v15.md
Normal file
259
docs/_posts/2016-04-07-react-v15.md
Normal file
@@ -0,0 +1,259 @@
|
||||
---
|
||||
title: "React v15.0"
|
||||
author: gaearon
|
||||
---
|
||||
|
||||
We would like to thank the React community for reporting issues and regressions in the release candidates on our [issue tracker](https://github.com/facebook/react/issues/). Over the last few weeks we fixed those issues, and now, after two release candidates, we are excited to finally release the stable version of React 15.
|
||||
|
||||
As a reminder, [we’re switching to major versions](/react/blog/2016/02/19/new-versioning-scheme.html) to indicate that we have been using React in production for a long time. This 15.0 release follows our previous 0.14 version and we’ll continue to follow semver like we’ve been doing since 2013. It’s also worth noting that [we no longer actively support Internet Explorer 8](/react/blog/2016/01/12/discontinuing-ie8-support.html). We believe React will work in its current form there but we will not be prioritizing any efforts to fix new issues that only affect IE8.
|
||||
|
||||
React 15 brings significant improvements to how we interact with the DOM:
|
||||
|
||||
* We are now using `document.createElement` instead of setting `innerHTML` when mounting components. This allows us to get rid of the `data-reactid` attribute on every node and make the DOM lighter. Using `document.createElement` is also faster in modern browsers and fixes a number of edge cases related to SVG elements and running multiple copies of React on the same page.
|
||||
|
||||
* Historically our support for SVG has been incomplete, and many tags and attributes were missing. We heard you, and in React 15 we [added support for all the SVG attributes that are recognized by today’s browsers](https://github.com/facebook/react/pull/6243). If we missed any of the attributes you’d like to use, please [let us know](https://github.com/facebook/react/issues/1657). As a bonus, thanks to using `document.createElement`, we no longer need to maintain a list of SVG tags, so any SVG tags that were previously unsupported should work just fine in React 15.
|
||||
|
||||
* We received some amazing contributions from the community in this release, and we would like to highlight [this pull request](https://github.com/facebook/react/pull/5753) by [Michael Wiencek](https://github.com/mwiencek) in particular. Thanks to Michael’s work, React 15 no longer emits extra `<span>` nodes around the text, making the DOM output much cleaner. This was a longstanding annoyance for React users so it’s exciting to accept this as an outside contribution.
|
||||
|
||||
While this isn’t directly related to the release, we understand that in order to receive more community contributions like Michael’s, we need to communicate our goals and priorities more openly, and review pull requests more decisively. As a first step towards this, we started publishing [React core team weekly meeting notes](https://github.com/reactjs/core-notes) again. We also intend to introduce an RFC process inspired by [Ember RFCs](https://github.com/emberjs/rfcs) so external contributors can have more insight and influence in the future development of React. We will keep you updated about this on our blog.
|
||||
|
||||
We are also experimenting with a new changelog format in this post. Every change now links to the corresponding pull request and mentions the author. Let us know whether you find this useful!
|
||||
|
||||
## Upgrade Guide
|
||||
|
||||
As usual with major releases, React 15 will remove support for some of the patterns deprecated nine months ago in React 0.14. We know changes can be painful (the Facebook codebase has over 20,000 React components, and that’s not even counting React Native), so we always try to make changes gradually in order to minimize the pain.
|
||||
|
||||
If your code is free of warnings when running under React 0.14, upgrading should be easy. The bulk of changes in this release are actually behind the scenes, impacting the way that React interacts with the DOM. The other substantial change is that React now supports the full range of SVG elements and attributes. Beyond that we have a large number of incremental improvements and additional warnings aimed to aid developers. We’ve also laid some groundwork in the core to bring you some new capabilities in future releases.
|
||||
|
||||
See the changelog below for more details.
|
||||
|
||||
## Installation
|
||||
|
||||
We recommend using React from `npm` and using a tool like browserify or webpack to build your code into a single bundle. To install the two packages:
|
||||
|
||||
* `npm install --save react react-dom`
|
||||
|
||||
Remember that by default, React runs extra checks and provides helpful warnings in development mode. When deploying your app, set the `NODE_ENV` environment variable to `production` to use the production build of React which does not include the development warnings and runs significantly faster.
|
||||
|
||||
If you can’t use `npm` yet, we provide pre-built browser builds for your convenience, which are also available in the `react` package on bower.
|
||||
|
||||
* **React**
|
||||
Dev build with warnings: <https://fb.me/react-15.0.0.js>
|
||||
Minified build for production: <https://fb.me/react-15.0.0.min.js>
|
||||
* **React with Add-Ons**
|
||||
Dev build with warnings: <https://fb.me/react-with-addons-15.0.0.js>
|
||||
Minified build for production: <https://fb.me/react-with-addons-15.0.0.min.js>
|
||||
* **React DOM** (include React in the page before React DOM)
|
||||
Dev build with warnings: <https://fb.me/react-dom-15.0.0.js>
|
||||
Minified build for production: <https://fb.me/react-dom-15.0.0.min.js>
|
||||
|
||||
## Changelog
|
||||
|
||||
### Major changes
|
||||
|
||||
- #### `document.createElement` is in and `data-reactid` is out
|
||||
|
||||
There were a number of large changes to our interactions with the DOM. One of the most noticeable changes is that we no longer set the `data-reactid` attribute for each DOM node. While this will make it more difficult to know if a website is using React, the advantage is that the DOM is much more lightweight. This change was made possible by us switching to use `document.createElement` on initial render. Previously we would generate a large string of HTML and then set `node.innerHTML`. At the time, this was decided to be faster than using `document.createElement` for the majority of cases and browsers that we supported. Browsers have continued to improve and so overwhelmingly this is no longer true. By using `createElement` we can make other parts of React faster. The ids were used to map back from events to the original React component, meaning we had to do a bunch of work on every event, even though we cached this data heavily. As we’ve all experienced, caching and in particularly invalidating caches, can be error prone and we saw many hard to reproduce issues over the years as a result. Now we can build up a direct mapping at render time since we already have a handle on the node.
|
||||
|
||||
**Note:** `data-reactid` is still present for server-rendered content, however it is much smaller than before and is simply an auto-incrementing counter.
|
||||
|
||||
<small>[@spicyj](https://github.com/spicyj) in [#5205](https://github.com/facebook/react/pull/5205)</small>
|
||||
|
||||
- #### No more extra `<span>`s
|
||||
|
||||
Another big change with our DOM interaction is how we render text blocks. Previously you may have noticed that React rendered a lot of extra `<span>`s. For example, in our most basic example on the home page we render `<div>Hello {this.props.name}</div>`, resulting in markup that contained 2 `<span>`s. Now we’ll render plain text nodes interspersed with comment nodes that are used for demarcation. This gives us the same ability to update individual pieces of text, without creating extra nested nodes. Very few people have depended on the actual markup generated here so it’s likely you are not impacted. However if you were targeting these `<span>`s in your CSS, you will need to adjust accordingly. You can always render them explicitly in your components.
|
||||
|
||||
<small>[@mwiencek](https://github.com/mwiencek) in [#5753](https://github.com/facebook/react/pull/5753)</small>
|
||||
|
||||
- #### Rendering `null` now uses comment nodes
|
||||
|
||||
We’ve also made use of these comment nodes to change what `null` renders to. Rendering to `null` was a feature we added in React 0.11 and was implemented by rendering `<noscript>` elements. By rendering to comment nodes now, there’s a chance some of your CSS will be targeting the wrong thing, specifically if you are making use of `:nth-child` selectors. React’s use of the `<noscript>` tag has always been considered an implementation detail of how React targets the DOM. We believe they are safe changes to make without going through a release with warnings detailing the subtle differences as they are details that should not be depended upon. Additionally, we have seen that these changes have improved React performance for many typical applications.
|
||||
|
||||
<small>[@spicyj](https://github.com/spicyj) in [#5451](https://github.com/facebook/react/pull/5451)</small>
|
||||
|
||||
- #### Functional components can now return `null` too
|
||||
|
||||
We added support for [defining stateless components as functions](/react/blog/2015/09/10/react-v0.14-rc1.html#stateless-function-components) in React 0.14. However, React 0.14 still allowed you to define a class component without extending `React.Component` or using `React.createClass()`, so [we couldn’t reliably tell if your component is a function or a class](https://github.com/facebook/react/issues/5355), and did not allow returning `null` from it. This issue is solved in React 15, and you can now return `null` from any component, whether it is a class or a function.
|
||||
|
||||
<small>[@jimfb](https://github.com/jimfb) in [#5884](https://github.com/facebook/react/pull/5884)</small>
|
||||
|
||||
- #### Improved SVG support
|
||||
|
||||
All SVG tags are now fully supported. (Uncommon SVG tags are not present on the `React.DOM` element helper, but JSX and `React.createElement` work on all tag names.) All SVG attributes that are implemented by the browsers should be supported too. If you find any attributes that we have missed, please [let us know in this issue](https://github.com/facebook/react/issues/1657).
|
||||
|
||||
<small>[@zpao](https://github.com/zpao) in [#6243](https://github.com/facebook/react/pull/6243)</small>
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- #### No more extra `<span>`s
|
||||
|
||||
It’s worth calling out the DOM structure changes above again, in particular the change from `<span>`s. In the course of updating the Facebook codebase, we found a very small amount of code that was depending on the markup that React generated. Some of these cases were integration tests like WebDriver which were doing very specific XPath queries to target nodes. Others were simply tests using `ReactDOM.renderToStaticMarkup` and comparing markup. Again, there were a very small number of changes that had to be made, but we don’t want anybody to be blindsided. We encourage everybody to run their test suites when upgrading and consider alternative approaches when possible. One approach that will work for some cases is to explicitly use `<span>`s in your `render` method.
|
||||
|
||||
<small>[@mwiencek](https://github.com/mwiencek) in [#5753](https://github.com/facebook/react/pull/5753)</small>
|
||||
|
||||
- #### `React.cloneElement()` now resolves `defaultProps`
|
||||
|
||||
We fixed a bug in `React.cloneElement()` that some components may rely on. If some of the `props` received by `cloneElement()` are `undefined`, it used to return an element with `undefined` values for those props. In React 15, we’re changing it to be consistent with `createElement()`. Now any `undefined` props passed to `cloneElement()` are resolved to the corresponding component’s `defaultProps`. Only one of our 20,000 React components was negatively affected by this so we feel comfortable releasing this change without keeping the old behavior for another release cycle.
|
||||
|
||||
<small>[@truongduy134](https://github.com/truongduy134) in [#5997](https://github.com/facebook/react/pull/5997)</small>
|
||||
|
||||
- #### `ReactPerf.getLastMeasurements()` is opaque
|
||||
|
||||
This change won’t affect applications but may break some third-party tools. We are [revamping `ReactPerf` implementation](https://github.com/facebook/react/pull/6046) and plan to release it during the 15.x cycle. The internal performance measurement format is subject to change so, for the time being, we consider the return value of `ReactPerf.getLastMeasurements()` an opaque data structure that should not be relied upon.
|
||||
|
||||
<small>[@gaearon](https://github.com/gaearon) in [#6286](https://github.com/facebook/react/pull/6286)</small>
|
||||
|
||||
- #### Removed deprecations
|
||||
|
||||
These deprecations were introduced nine months ago in v0.14 with a warning and are removed:
|
||||
|
||||
- Deprecated APIs are removed from the `React` top-level export: `findDOMNode`, `render`, `renderToString`, `renderToStaticMarkup`, and `unmountComponentAtNode`. As a reminder, they are now available on `ReactDOM` and `ReactDOMServer`.
|
||||
<small>[@jimfb](https://github.com/jimfb) in [#5832](https://github.com/facebook/react/pull/5832)</small>
|
||||
|
||||
- Deprecated addons are removed: `batchedUpdates` and `cloneWithProps`.
|
||||
<small>[@jimfb](https://github.com/jimfb) in [#5859](https://github.com/facebook/react/pull/5859), [@zpao](https://github.com/zpao) in [#6016](https://github.com/facebook/react/pull/6016)</small>
|
||||
|
||||
- Deprecated component instance methods are removed: `setProps`, `replaceProps`, and `getDOMNode`.
|
||||
<small>[@jimfb](https://github.com/jimfb) in [#5570](https://github.com/facebook/react/pull/5570)</small>
|
||||
|
||||
- Deprecated CommonJS `react/addons` entry point is removed. As a reminder, you should use separate `react-addons-*` packages instead. This only applies if you use the CommonJS builds.
|
||||
<small>[@gaearon](https://github.com/gaearon) in [#6285](https://github.com/facebook/react/pull/6285)</small>
|
||||
|
||||
- Passing `children` to void elements like `<input>` was deprecated, and now throws an error.
|
||||
<small>[@jonhester](https://github.com/jonhester) in [#3372](https://github.com/facebook/react/pull/3372)</small>
|
||||
|
||||
- React-specific properties on DOM `refs` (e.g. `this.refs.div.props`) were deprecated, and are removed now.
|
||||
<small>[@jimfb](https://github.com/jimfb) in [#5495](https://github.com/facebook/react/pull/5495)</small>
|
||||
|
||||
### New deprecations, introduced with a warning
|
||||
|
||||
Each of these changes will continue to work as before with a new warning until the release of React 16 so you can upgrade your code gradually.
|
||||
|
||||
- `LinkedStateMixin` and `valueLink` are now deprecated due to very low popularity. If you need this, you can use a wrapper component that implements the same behavior: [react-linked-input](https://www.npmjs.com/package/react-linked-input).
|
||||
<small>[@jimfb](https://github.com/jimfb) in [#6127](https://github.com/facebook/react/pull/6127)</small>
|
||||
|
||||
- Future versions of React will treat `<input value={null}>` as a request to clear the input. However, React 0.14 has been ignoring `value={null}`. React 15 warns you on a `null` input value and offers you to clarify your intention. To fix the warning, you may explicitly pass an empty string to clear a controlled input, or pass `undefined` to make the input uncontrolled.
|
||||
<small>[@antoaravinth](https://github.com/antoaravinth) in [#5048](https://github.com/facebook/react/pull/5048)</small>
|
||||
|
||||
- `ReactPerf.printDOM()` was renamed to `ReactPerf.printOperations()`, and `ReactPerf.getMeasurementsSummaryMap()` was renamed to `ReactPerf.getWasted()`.
|
||||
<small>[@gaearon](https://github.com/gaearon) in [#6287](https://github.com/facebook/react/pull/6287)</small>
|
||||
|
||||
### New helpful warnings
|
||||
|
||||
- If you use a minified copy of the _development_ build, React DOM kindly encourages you to use the faster production build instead.
|
||||
<small>[@spicyj](https://github.com/spicyj) in [#5083](https://github.com/facebook/react/pull/5083)</small>
|
||||
|
||||
- React DOM: When specifying a unit-less CSS value as a string, a future version will not add `px` automatically. This version now warns in this case (ex: writing `style={{'{{'}}width: '300'}}`. Unitless *number* values like `width: 300` are unchanged.
|
||||
<small>[@pluma](https://github.com/pluma) in [#5140](https://github.com/facebook/react/pull/5140)</small>
|
||||
|
||||
- Synthetic Events will now warn when setting and accessing properties (which will not get cleared appropriately), as well as warn on access after an event has been returned to the pool.
|
||||
<small>[@kentcdodds](https://github.com/kentcdodds) in [#5940](https://github.com/facebook/react/pull/5940) and [@koba04](https://github.com/koba04) in [#5947](https://github.com/facebook/react/pull/5947)</small>
|
||||
|
||||
- Elements will now warn when attempting to read `ref` and `key` from the props.
|
||||
<small>[@prometheansacrifice](https://github.com/prometheansacrifice) in [#5744](https://github.com/facebook/react/pull/5744)</small>
|
||||
|
||||
- React will now warn if you pass a different `props` object to `super()` in the constructor.
|
||||
<small>[@prometheansacrifice](https://github.com/prometheansacrifice) in [#5346](https://github.com/facebook/react/pull/5346)</small>
|
||||
|
||||
- React will now warn if you call `setState()` inside `getChildContext()`.
|
||||
<small>[@raineroviir](https://github.com/raineroviir) in [#6121](https://github.com/facebook/react/pull/6121)</small>
|
||||
|
||||
- React DOM now attempts to warn for mistyped event handlers on DOM elements, such as `onclick` which should be `onClick`.
|
||||
<small>[@ali](https://github.com/ali) in [#5361](https://github.com/facebook/react/pull/5361)</small>
|
||||
|
||||
- React DOM now warns about `NaN` values in `style`.
|
||||
<small>[@jontewks](https://github.com/jontewks) in [#5811](https://github.com/facebook/react/pull/5811)</small>
|
||||
|
||||
- React DOM now warns if you specify both `value` and `defaultValue` for an input.
|
||||
<small>[@mgmcdermott](https://github.com/mgmcdermott) in [#5823](https://github.com/facebook/react/pull/5823)</small>
|
||||
|
||||
- React DOM now warns if an input switches between being controlled and uncontrolled.
|
||||
<small>[@TheBlasfem](https://github.com/TheBlasfem) in [#5864](https://github.com/facebook/react/pull/5864)</small>
|
||||
|
||||
- React DOM now warns if you specify `onFocusIn` or `onFocusOut` handlers as they are unnecessary in React.
|
||||
<small>[@jontewks](https://github.com/jontewks) in [#6296](https://github.com/facebook/react/pull/6296)</small>
|
||||
|
||||
- React now prints a descriptive error message when you pass an invalid callback as the last argument to `ReactDOM.render()`, `this.setState()`, or `this.forceUpdate()`.
|
||||
<small>[@conorhastings](https://github.com/conorhastings) in [#5193](https://github.com/facebook/react/pull/5193) and [@gaearon](https://github.com/gaearon) in [#6310](https://github.com/facebook/react/pull/6310)</small>
|
||||
|
||||
- Add-Ons: `TestUtils.Simulate()` now prints a helpful message if you attempt to use it with shallow rendering.
|
||||
<small>[@conorhastings](https://github.com/conorhastings) in [#5358](https://github.com/facebook/react/pull/5358)</small>
|
||||
|
||||
- PropTypes: `arrayOf()` and `objectOf()` provide better error messages for invalid arguments.
|
||||
<small>[@chicoxyzzy](https://github.com/chicoxyzzy) in [#5390](https://github.com/facebook/react/pull/5390)</small>
|
||||
|
||||
### Notable bug fixes
|
||||
|
||||
- Fixed multiple small memory leaks.
|
||||
<small>[@spicyj](https://github.com/spicyj) in [#4983](https://github.com/facebook/react/pull/4983) and [@victor-homyakov](https://github.com/victor-homyakov) in [#6309](https://github.com/facebook/react/pull/6309)</small>
|
||||
|
||||
- Input events are handled more reliably in IE 10 and IE 11; spurious events no longer fire when using a placeholder.
|
||||
<small>[@jquense](https://github.com/jquense) in [#4051](https://github.com/facebook/react/pull/4051)</small>
|
||||
|
||||
- The `componentWillReceiveProps()` lifecycle method is now consistently called when `context` changes.
|
||||
<small>[@milesj](https://github.com/milesj) in [#5787](https://github.com/facebook/react/pull/5787)</small>
|
||||
|
||||
- `React.cloneElement()` doesn’t append slash to an existing `key` when used inside `React.Children.map()`.
|
||||
<small>[@ianobermiller](https://github.com/ianobermiller) in [#5892](https://github.com/facebook/react/pull/5892)</small>
|
||||
|
||||
- React DOM now supports the `cite` and `profile` HTML attributes.
|
||||
<small>[@AprilArcus](https://github.com/AprilArcus) in [#6094](https://github.com/facebook/react/pull/6094) and [@saiichihashimoto](https://github.com/saiichihashimoto) in [#6032](https://github.com/facebook/react/pull/6032)</small>
|
||||
|
||||
- React DOM now supports `cssFloat`, `gridRow` and `gridColumn` CSS properties.
|
||||
<small>[@stevenvachon](https://github.com/stevenvachon) in [#6133](https://github.com/facebook/react/pull/6133) and [@mnordick](https://github.com/mnordick) in [#4779](https://github.com/facebook/react/pull/4779)</small>
|
||||
|
||||
- React DOM now correctly handles `borderImageOutset`, `borderImageWidth`, `borderImageSlice`, `floodOpacity`, `strokeDasharray`, and `strokeMiterlimit` as unitless CSS properties.
|
||||
<small>[@rofrischmann](https://github.com/rofrischmann) in [#6210](https://github.com/facebook/react/pull/6210) and [#6270](https://github.com/facebook/react/pull/6270)</small>
|
||||
|
||||
- React DOM now supports the `onAnimationStart`, `onAnimationEnd`, `onAnimationIteration`, `onTransitionEnd`, and `onInvalid` events. Support for `onLoad` has been added to `object` elements.
|
||||
<small>[@tomduncalf](https://github.com/tomduncalf) in [#5187](https://github.com/facebook/react/pull/5187), [@milesj](https://github.com/milesj) in [#6005](https://github.com/facebook/react/pull/6005), and [@ara4n](https://github.com/ara4n) in [#5781](https://github.com/facebook/react/pull/5781)</small>
|
||||
|
||||
- React DOM now defaults to using DOM attributes instead of properties, which fixes a few edge case bugs. Additionally the nullification of values (ex: `href={null}`) now results in the forceful removal, no longer trying to set to the default value used by browsers in the absence of a value.
|
||||
<small>[@syranide](https://github.com/syranide) in [#1510](https://github.com/facebook/react/pull/1510)</small>
|
||||
|
||||
- React DOM does not mistakingly coerce `children` to strings for Web Components.
|
||||
<small>[@jimfb](https://github.com/jimfb) in [#5093](https://github.com/facebook/react/pull/5093)</small>
|
||||
|
||||
- React DOM now correctly normalizes SVG `<use>` events.
|
||||
<small>[@edmellum](https://github.com/edmellum) in [#5720](https://github.com/facebook/react/pull/5720)</small>
|
||||
|
||||
- React DOM does not throw if a `<select>` is unmounted while its `onChange` handler is executing.
|
||||
<small>[@sambev](https://github.com/sambev) in [#6028](https://github.com/facebook/react/pull/6028)</small>
|
||||
|
||||
- React DOM does not throw in Windows 8 apps.
|
||||
<small>[@Andrew8xx8](https://github.com/Andrew8xx8) in [#6063](https://github.com/facebook/react/pull/6063)</small>
|
||||
|
||||
- React DOM does not throw when asynchronously unmounting a child with a `ref`.
|
||||
<small>[@yiminghe](https://github.com/yiminghe) in [#6095](https://github.com/facebook/react/pull/6095)</small>
|
||||
|
||||
- React DOM no longer forces synchronous layout because of scroll position tracking.
|
||||
<small>[@syranide](https://github.com/syranide) in [#2271](https://github.com/facebook/react/pull/2271)</small>
|
||||
|
||||
- `Object.is` is used in a number of places to compare values, which leads to fewer false positives, especially involving `NaN`. In particular, this affects the `shallowCompare` add-on.
|
||||
<small>[@chicoxyzzy](https://github.com/chicoxyzzy) in [#6132](https://github.com/facebook/react/pull/6132)</small>
|
||||
|
||||
- Add-Ons: ReactPerf no longer instruments adding or removing an event listener because they don’t really touch the DOM due to event delegation.
|
||||
<small>[@antoaravinth](https://github.com/antoaravinth) in [#5209](https://github.com/facebook/react/pull/5209)</small>
|
||||
|
||||
### Other improvements
|
||||
|
||||
- React now uses `loose-envify` instead of `envify` so it installs less transitive dependencies.
|
||||
<small>[@qerub](https://github.com/qerub) in [#6303](https://github.com/facebook/react/pull/6303)</small>
|
||||
|
||||
- Shallow renderer now exposes `getMountedInstance()`.
|
||||
<small>[@glenjamin](https://github.com/glenjamin) in [#4918](https://github.com/facebook/react/pull/4918)</small>
|
||||
|
||||
- Shallow renderer now returns the rendered output from `render()`.
|
||||
<small>[@simonewebdesign](https://github.com/simonewebdesign) in [#5411](https://github.com/facebook/react/pull/5411)</small>
|
||||
|
||||
- React no longer depends on ES5 *shams* for `Object.create` and `Object.freeze` in older environments. It still, however, requires ES5 *shims* in those environments.
|
||||
<small>[@dgreensp](https://github.com/dgreensp) in [#4959](https://github.com/facebook/react/pull/4959)</small>
|
||||
|
||||
- React DOM now allows `data-` attributes with names that start with numbers.
|
||||
<small>[@nLight](https://github.com/nLight) in [#5216](https://github.com/facebook/react/pull/5216)</small>
|
||||
|
||||
- React DOM adds a new `suppressContentEditableWarning` prop for components like [Draft.js](https://facebook.github.io/draft-js/) that intentionally manage `contentEditable` children with React.
|
||||
<small>[@mxstbr](https://github.com/mxstbr) in [#6112](https://github.com/facebook/react/pull/6112)</small>
|
||||
|
||||
- React improves the performance for `createClass()` on complex specs.
|
||||
<small>[@spicyj](https://github.com/spicyj) in [#5550](https://github.com/facebook/react/pull/5550)</small>
|
||||
34
docs/_posts/2016-04-08-react-v15.0.1.md
Normal file
34
docs/_posts/2016-04-08-react-v15.0.1.md
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: "React v15.0.1"
|
||||
author: zpao
|
||||
---
|
||||
|
||||
Yesterday afternoon we shipped v15.0.0 and quickly got some feedback about a couple of issues. We apologize for these problems and we've been working since then to make sure we get fixes into your hands as quickly as possible.
|
||||
|
||||
The first of these issues is related to the removal of an undocumented API. This API was added to enable [JSX Spread Attributes](/react/docs/jsx-spread.html) in our JS compile tools (react-tools, JSXTransformer) before `Object.assign` was standard. When we stopped supporting these tools last year, we kept the API there to catch the longer tail of people using those tools. Meanwhile we moved to using Babel and encouraged others to do the same. Babel will typically compile the spread use to an `_extends` helper, which will use `Object.assign`. We did not properly research other compilation tools before deciding to remove the API in v15. Specifically, TypeScript and coffee-react are two popular packages using `React.__spread`, as well as reactify which still makes use react-tools. In order to make sure that code compiled with these tools is not broken, we will be restoring the `React.__spread` API and adding a warning. It will be removed in the future so if you maintain a project making using of it, we encourage you to compile to `Object.assign` directly or a similar helper function.
|
||||
|
||||
The second issue resulted in cursor position being lost in controlled inputs. We merged a pull request earlier this week to fix a separate regression from v0.14. Our goal was to target `<option>` elements but we ended up targeting all interactions with `value` properties. Unfortunately we didn't test it as thoroughly as we thought. We backed out the offending change and fixed the issue in different way which doesn't have the same problem.
|
||||
|
||||
We apologize if you installed 15.0.0 and have encountered these issues yourselves.
|
||||
|
||||
As usual, you can get install the `react` package via npm or download a browser bundle.
|
||||
|
||||
* **React**
|
||||
Dev build with warnings: <https://fb.me/react-15.0.1.js>
|
||||
Minified build for production: <https://fb.me/react-15.0.1.min.js>
|
||||
* **React with Add-Ons**
|
||||
Dev build with warnings: <https://fb.me/react-with-addons-15.0.1.js>
|
||||
Minified build for production: <https://fb.me/react-with-addons-15.0.1.min.js>
|
||||
* **React DOM** (include React in the page before React DOM)
|
||||
Dev build with warnings: <https://fb.me/react-dom-15.0.1.js>
|
||||
Minified build for production: <https://fb.me/react-dom-15.0.1.min.js>
|
||||
|
||||
## Changelog
|
||||
|
||||
### React
|
||||
- Restore `React.__spread` API to unbreak code compiled with some tools making use of this undocumented API. It is now officially deprecated.
|
||||
<small>[@zpao](https://github.com/zpao) in [#6444](https://github.com/facebook/react/pull/6444)</small>
|
||||
|
||||
### ReactDOM
|
||||
- Fixed issue resulting in loss of cursor position in controlled inputs.
|
||||
<small>[@spicyj](https://github.com/spicyj) in [#6449](https://github.com/facebook/react/pull/6449)</small>
|
||||
@@ -299,4 +299,8 @@ However, you may still specify `.propTypes` and `.defaultProps` by setting them
|
||||
>
|
||||
> Because stateless functions don't have a backing instance, you can't attach a ref to a stateless function component. Normally this isn't an issue, since stateless functions do not provide an imperative API. Without an imperative API, there isn't much you could do with an instance anyway. However, if a user wants to find the DOM node of a stateless function component, they must wrap the component in a stateful component (eg. ES6 class component) and attach the ref to the stateful wrapper component.
|
||||
|
||||
> NOTE:
|
||||
>
|
||||
> In React v0.14, stateless functional components were not permitted to return `null` or `false` (a workaround is to return a `<noscript />` instead). This was fixed in React v15, and stateless functional components are now permitted to return `null`.
|
||||
|
||||
In an ideal world, most of your components would be stateless functions because in the future we’ll also be able to make performance optimizations specific to these components by avoiding unnecessary checks and memory allocations. This is the recommended pattern, when possible.
|
||||
|
||||
@@ -3,78 +3,11 @@ id: tooling-integration
|
||||
title: Tooling Integration
|
||||
permalink: tooling-integration.html
|
||||
prev: more-about-refs.html
|
||||
next: addons.html
|
||||
next: language-tooling.html
|
||||
---
|
||||
|
||||
Every project uses a different system for building and deploying JavaScript. We've tried to make React as environment-agnostic as possible.
|
||||
We've tried to make React as environment-agnostic as possible. People use React in a variety of languages (JavaScript, TypeScript, ClojureScript, etc) and in a variety of environments (web, iOS, Android, NodeJS, Nashorn, etc). There are many tools to help you build great applications. In these sections we introduce some of the tools that are most commonly used together with React.
|
||||
|
||||
## React
|
||||
|
||||
### CDN-hosted React
|
||||
|
||||
We provide CDN-hosted versions of React [on our download page](/react/downloads.html). These pre-built files use the UMD module format. Dropping them in with a simple `<script>` tag will inject a `React` global into your environment. It should also work out-of-the-box in CommonJS and AMD environments.
|
||||
|
||||
### Using master
|
||||
|
||||
We have instructions for building from `master` [in our GitHub repository](https://github.com/facebook/react). We build a tree of CommonJS modules under `build/modules` which you can drop into any environment or packaging tool that supports CommonJS.
|
||||
|
||||
## JSX
|
||||
|
||||
### In-browser JSX Transform
|
||||
|
||||
If you like using JSX, Babel 5 provided an in-browser ES6 and JSX transformer for development called browser.js that can be included from [CDNJS](http://cdnjs.com/libraries/babel-core/5.8.34). Include a `<script type="text/babel">` tag to engage the JSX transformer.
|
||||
|
||||
> Note:
|
||||
>
|
||||
> The in-browser JSX transformer is fairly large and results in extraneous computation client-side that can be avoided. Do not use it in production — see the next section.
|
||||
|
||||
### Productionizing: Precompiled JSX
|
||||
|
||||
If you have [npm](https://www.npmjs.com/), you can run `npm install -g babel-cli`. Babel has built-in support for React v0.12+. Tags are automatically transformed to their equivalent `React.createElement(...)`, `displayName` is automatically inferred and added to all `React.createClass` calls.
|
||||
|
||||
This tool will translate files that use JSX syntax to plain JavaScript files that can run directly in the browser. It will also watch directories for you and automatically transform files when they are changed; for example: `babel --watch src/ --out-dir lib/`.
|
||||
|
||||
Beginning with Babel 6, there are no transforms included by default. This means that options must be specified when running the `babel` command, or a `.babelrc` must specify options. Additional packages must also be installed which bundle together a number of transforms, called presets. The most common use when working with React will be to include the `es2015` and `react` presets. More information about the changes to Babel can be found in [their blog post announcing Babel 6](http://babeljs.io/blog/2015/10/29/6.0.0/).
|
||||
|
||||
Here is an example of what you will do if using ES2015 syntax and React:
|
||||
|
||||
```
|
||||
npm install babel-preset-es2015 babel-preset-react
|
||||
babel --presets es2015,react --watch src/ --out-dir lib/
|
||||
```
|
||||
|
||||
By default JSX files with a `.js` extension are transformed. Run `babel --help` for more information on how to use Babel.
|
||||
|
||||
Example output:
|
||||
|
||||
```
|
||||
$ cat test.jsx
|
||||
var HelloMessage = React.createClass({
|
||||
render: function() {
|
||||
return <div>Hello {this.props.name}</div>;
|
||||
}
|
||||
});
|
||||
|
||||
ReactDOM.render(<HelloMessage name="John" />, mountNode);
|
||||
$ babel test.jsx
|
||||
"use strict";
|
||||
|
||||
var HelloMessage = React.createClass({
|
||||
displayName: "HelloMessage",
|
||||
|
||||
render: function render() {
|
||||
return React.createElement(
|
||||
"div",
|
||||
null,
|
||||
"Hello ",
|
||||
this.props.name
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ReactDOM.render(React.createElement(HelloMessage, { name: "John" }), mountNode);
|
||||
```
|
||||
|
||||
### Helpful Open-Source Projects
|
||||
|
||||
The open-source community has built tools that integrate JSX with several editors and build systems. See [JSX integrations](https://github.com/facebook/react/wiki/Complementary-Tools#jsx-integrations) for the full list.
|
||||
* [Language Tooling](/react/docs/language-tooling.html) describes how to set up tools like Babel to transpile JSX for a better development experience.
|
||||
* [Package Management](/react/docs/package-management.html) describes how to configure React as a dependency of your project.
|
||||
* [Server-side Environments](/react/docs/environments.html) describes how to configure your environment for server-side rendering with React.
|
||||
|
||||
79
docs/docs/09.1-language-tooling.md
Normal file
79
docs/docs/09.1-language-tooling.md
Normal file
@@ -0,0 +1,79 @@
|
||||
---
|
||||
id: language-tooling
|
||||
title: Language Tooling
|
||||
permalink: language-tooling.html
|
||||
prev: tooling-integration.html
|
||||
next: package-management.html
|
||||
---
|
||||
|
||||
## ES2015 with JSX
|
||||
|
||||
### In-browser JSX Transform
|
||||
|
||||
If you like using JSX, Babel 5 provided an in-browser ES2015 and JSX transformer for development called browser.js that can be included from [CDNJS](https://cdnjs.com/libraries/babel-core/5.8.34). Include a `<script type="text/babel">` tag to engage the JSX transformer.
|
||||
|
||||
> Note:
|
||||
>
|
||||
> The in-browser JSX transformer is fairly large and results in extraneous computation client-side that can be avoided. Do not use it in production — see the next section.
|
||||
|
||||
### Productionizing: Precompiled JSX
|
||||
|
||||
If you have [npm](https://www.npmjs.com/), you can run `npm install -g babel-cli`. Babel has built-in support for React v0.12+. Tags are automatically transformed to their equivalent `React.createElement(...)`, `displayName` is automatically inferred and added to all `React.createClass` calls.
|
||||
|
||||
This tool will translate files that use JSX syntax to plain JavaScript files that can run directly in the browser. It will also watch directories for you and automatically transform files when they are changed; for example: `babel --watch src/ --out-dir lib/`.
|
||||
|
||||
Beginning with Babel 6, there are no transforms included by default. This means that options must be specified when running the `babel` command, or a `.babelrc` must specify options. Additional packages must also be installed which bundle together a number of transforms, called presets. The most common use when working with React will be to include the `es2015` and `react` presets. More information about the changes to Babel can be found in [their blog post announcing Babel 6](http://babeljs.io/blog/2015/10/29/6.0.0).
|
||||
|
||||
Here is an example of what you will do if using ES2015 syntax and React:
|
||||
|
||||
```
|
||||
npm install babel-preset-es2015 babel-preset-react
|
||||
babel --presets es2015,react --watch src/ --out-dir lib/
|
||||
```
|
||||
|
||||
By default JSX files with a `.js` extension are transformed. Run `babel --help` for more information on how to use Babel.
|
||||
|
||||
Example output:
|
||||
|
||||
```
|
||||
$ cat test.js
|
||||
var HelloMessage = React.createClass({
|
||||
render: function() {
|
||||
return <div>Hello {this.props.name}</div>;
|
||||
}
|
||||
});
|
||||
|
||||
ReactDOM.render(<HelloMessage name="John" />, mountNode);
|
||||
$ babel test.js
|
||||
"use strict";
|
||||
|
||||
var HelloMessage = React.createClass({
|
||||
displayName: "HelloMessage",
|
||||
|
||||
render: function render() {
|
||||
return React.createElement(
|
||||
"div",
|
||||
null,
|
||||
"Hello ",
|
||||
this.props.name
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ReactDOM.render(React.createElement(HelloMessage, { name: "John" }), mountNode);
|
||||
```
|
||||
|
||||
### Helpful Open-Source Projects
|
||||
|
||||
The open-source community has built tools that integrate JSX with several editors and build systems. See [JSX integrations](https://github.com/facebook/react/wiki/Complementary-Tools#jsx-integrations) for the full list.
|
||||
|
||||
## Flow
|
||||
|
||||
Flow is a JavaScript type checker released by Facebook, and it supports JSX. For more info, checkout the [Flow homepage](http://flowtype.org/).
|
||||
|
||||
|
||||
## TypeScript
|
||||
|
||||
Microsoft TypeScript now supports JSX. For more info, check out the [TypeScript JSX Documentation](http://www.typescriptlang.org/docs/handbook/jsx.html) or their guide on [getting started with react+webpack](https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/quick-start/react-webpack.md).
|
||||
|
||||
|
||||
126
docs/docs/09.2-package-management.md
Normal file
126
docs/docs/09.2-package-management.md
Normal file
@@ -0,0 +1,126 @@
|
||||
---
|
||||
id: package-management
|
||||
title: Package Management
|
||||
permalink: package-management.html
|
||||
prev: language-tooling.html
|
||||
next: environments.html
|
||||
---
|
||||
|
||||
## CDN-hosted React
|
||||
|
||||
We provide CDN-hosted versions of React [on our download page](/react/downloads.html). These pre-built files use the UMD module format. Dropping them in with a simple `<script>` tag will inject the `React` and `ReactDOM` globals into your environment. It should also work out-of-the-box in CommonJS and AMD environments.
|
||||
|
||||
|
||||
## Using React from npm
|
||||
|
||||
You can use React with a CommonJS module system like [browserify](http://browserify.org/) or [webpack](https://webpack.github.io/). Use the [`react`](https://www.npmjs.com/package/react) and [`react-dom`](https://www.npmjs.com/package/react-dom) npm packages.
|
||||
|
||||
```js
|
||||
// main.js
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
|
||||
ReactDOM.render(
|
||||
<h1>Hello, world!</h1>,
|
||||
document.getElementById('example')
|
||||
);
|
||||
```
|
||||
|
||||
Configure [babel](https://babeljs.io/) with a `.babelrc` file:
|
||||
|
||||
```json
|
||||
{ "presets": ["react"] }
|
||||
```
|
||||
|
||||
> Note:
|
||||
>
|
||||
> If you are using ES2015, you will want to also use the `babel-preset-es2015` package.
|
||||
|
||||
|
||||
To install React DOM and build your bundle with browserify:
|
||||
|
||||
```sh
|
||||
$ npm install --save react react-dom babelify babel-preset-react
|
||||
$ browserify -t [ babelify ] main.js -o bundle.js
|
||||
```
|
||||
|
||||
To install React DOM and build your bundle with webpack:
|
||||
|
||||
```sh
|
||||
$ npm install --save react react-dom babel-preset-react babel-loader babel-core
|
||||
$ webpack main.js bundle.js --module-bind 'js=babel-loader'
|
||||
```
|
||||
|
||||
> Note:
|
||||
>
|
||||
> If you are using ES2015, you will want to also use the `babel-preset-es2015` package.
|
||||
|
||||
**Note:** by default, React will be in development mode, which is slower, and not advised for production. To use React in production mode, set the environment variable `NODE_ENV` to `production` (using envify or webpack's DefinePlugin). For example:
|
||||
|
||||
```js
|
||||
new webpack.DefinePlugin({
|
||||
"process.env": {
|
||||
NODE_ENV: JSON.stringify("production")
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Update your HTML file as below:
|
||||
|
||||
```html{8,12}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Hello React!</title>
|
||||
<script src="build/react.js"></script>
|
||||
<script src="build/react-dom.js"></script>
|
||||
<!-- No need for Babel! -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="example"></div>
|
||||
<script src="build/helloworld.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Using React from Bower
|
||||
|
||||
Bower is a package manager optimized for the front-end development. If multiple packages depend on a package - jQuery for example - Bower will download jQuery just once. This is known as a flat dependency graph and it helps reduce page load. For more info, visit http://bower.io/
|
||||
|
||||
If you'd like to use bower, it's as easy as:
|
||||
|
||||
```
|
||||
bower install --save react
|
||||
```
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
|
||||
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Hello React!</title>
|
||||
<script src="bower_components/react/react.js"></script>
|
||||
<script src="bower_components/react/react-dom.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="example"></div>
|
||||
<script type="text/babel">
|
||||
ReactDOM.render(
|
||||
<h1>Hello, world!</h1>,
|
||||
document.getElementById('example')
|
||||
);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
## Using master
|
||||
|
||||
We have instructions for building from `master` [in our GitHub repository](https://github.com/facebook/react).
|
||||
|
||||
75
docs/docs/09.3-environments.md
Normal file
75
docs/docs/09.3-environments.md
Normal file
@@ -0,0 +1,75 @@
|
||||
---
|
||||
id: environments
|
||||
title: Server-side Environments
|
||||
permalink: environments.html
|
||||
prev: package-management.html
|
||||
next: addons.html
|
||||
---
|
||||
|
||||
One of the great things about React is that it doesn't require the DOM as a dependency, which means it is possible to render a React application on the server and send the HTML markup down to the client. There are a few things that React expects, so this guide will help you get started in your preferred environment.
|
||||
|
||||
|
||||
## Node.js
|
||||
|
||||
[Node.js](http://nodejs.org/) is a popular JavaScript runtime that comes with an extensive core library and support for installing packages from npm to expand on the basic functionality. As we've described elsewhere in the documentation, you can install `react` and `react-dom` from npm.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
var React = require('react');
|
||||
var ReactDOMServer = require('react-dom/server');
|
||||
|
||||
var element = React.createElement('div', null, 'Hello World!');
|
||||
console.log(ReactDOMServer.renderToString(element));
|
||||
```
|
||||
|
||||
If you use JSX, you may want to pre-compile your components. Alternatively you may want to consider using [Babel's require hook](https://babeljs.io/docs/usage/require/) or [`babel-node`](https://babeljs.io/docs/usage/cli/#babel-node).
|
||||
|
||||
> Note:
|
||||
>
|
||||
> Some versions of Node.js have an `Object.assign` implementation that does not preserve key order. This can cause errors when validating the markup, creating a warning that says "React attempted to reuse markup in a container but the checksum was invalid". If you run into this issue, you can override `Object.assign` to use a polyfill that preserves key order. For more details, see [Issue #6451](https://github.com/facebook/react/issues/6451).
|
||||
|
||||
|
||||
## Nashorn
|
||||
|
||||
Nashorn is a lightweight high-performance JavaScript runtime that runs within the JVM. React should run out of the box in Java 8+.
|
||||
|
||||
Example:
|
||||
|
||||
```java
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.FileReader;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
public class ReactRender
|
||||
{
|
||||
public static void main(String[] args) throws ScriptException, IOException {
|
||||
ScriptEngine nashorn = new ScriptEngineManager().getEngineByName("nashorn");
|
||||
|
||||
// These files can be downloaded as a part of the starter kit
|
||||
// from https://facebook.github.io/react
|
||||
nashorn.eval(new FileReader("path/to/react.js"));
|
||||
nashorn.eval(new FileReader("path/to/react-dom-server.js"));
|
||||
|
||||
System.out.println(nashorn.eval(
|
||||
"ReactDOMServer.renderToString(" +
|
||||
"React.createElement('div', null, 'Hello World!')" +
|
||||
");"
|
||||
));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If your application uses npm packages, or you want to transform JSX in Nashorn, you will need to do some additional environment setup. The following resources may be helpful in getting you started:
|
||||
|
||||
* [http://winterbe.com/posts/2015/02/16/isomorphic-react-webapps-on-the-jvm/](http://winterbe.com/posts/2015/02/16/isomorphic-react-webapps-on-the-jvm/)
|
||||
* [https://github.com/nodyn/jvm-npm](https://github.com/nodyn/jvm-npm)
|
||||
* [https://gist.github.com/aesteve/883e0fd33390451cb8eb](https://gist.github.com/aesteve/883e0fd33390451cb8eb)
|
||||
|
||||
> Note:
|
||||
>
|
||||
> Using Babel within Nashorn will require Java 8u72+, as update 72 fixed [JDK-8135190](https://bugs.openjdk.java.net/browse/JDK-8135190).
|
||||
@@ -2,7 +2,7 @@
|
||||
id: addons
|
||||
title: Add-ons
|
||||
permalink: addons.html
|
||||
prev: tooling-integration.html
|
||||
prev: environments.html
|
||||
next: animation.html
|
||||
---
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@ next: clone-with-props.html
|
||||
var ReactTestUtils = require('react-addons-test-utils');
|
||||
```
|
||||
|
||||
> Note:
|
||||
>
|
||||
> Airbnb has released a testing utility called Enzyme, which makes it easy to assert, manipulate, and traverse your React Components' output. If you're deciding on a unit testing library, it's worth checking out: [http://airbnb.io/enzyme/](http://airbnb.io/enzyme/)
|
||||
|
||||
### Simulate
|
||||
|
||||
```javascript
|
||||
|
||||
@@ -12,52 +12,10 @@ The easiest way to start hacking on React is using the following JSFiddle Hello
|
||||
* **[React JSFiddle](https://jsfiddle.net/reactjs/69z2wepo/)**
|
||||
* [React JSFiddle without JSX](https://jsfiddle.net/reactjs/5vjqabv3/)
|
||||
|
||||
## Using React from npm
|
||||
|
||||
We recommend using React with a CommonJS module system like [browserify](http://browserify.org/) or [webpack](https://webpack.github.io/). Use the [`react`](https://www.npmjs.com/package/react) and [`react-dom`](https://www.npmjs.com/package/react-dom) npm packages.
|
||||
## Starter Pack
|
||||
|
||||
```js
|
||||
// main.js
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
|
||||
ReactDOM.render(
|
||||
<h1>Hello, world!</h1>,
|
||||
document.getElementById('example')
|
||||
);
|
||||
```
|
||||
|
||||
To install React DOM and build your bundle with browserify:
|
||||
|
||||
```sh
|
||||
$ npm install --save react react-dom babelify babel-preset-react
|
||||
$ browserify -t [ babelify --presets [ react ] ] main.js -o bundle.js
|
||||
```
|
||||
|
||||
To install React DOM and build your bundle with webpack:
|
||||
|
||||
```sh
|
||||
$ npm install --save react react-dom babel-preset-react
|
||||
$ webpack
|
||||
```
|
||||
|
||||
> Note:
|
||||
>
|
||||
> If you are using ES2015, you will want to also use the `babel-preset-es2015` package.
|
||||
|
||||
**Note:** by default, React will be in development mode, which is slower, and not advised for production. To use React in production mode, set the environment variable `NODE_ENV` to `production` (using envify or webpack's DefinePlugin). For example:
|
||||
|
||||
```js
|
||||
new webpack.DefinePlugin({
|
||||
"process.env": {
|
||||
NODE_ENV: JSON.stringify("production")
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Quick Start Without npm
|
||||
|
||||
If you're not ready to use npm yet, you can download the starter kit which includes prebuilt copies of React and React DOM.
|
||||
If you're just getting started, you can download the starter kit. The starter kit includes prebuilt copies of React and React DOM for the browser, as well as a collection of usage examples to help you get started.
|
||||
|
||||
<div class="buttons-unit downloads">
|
||||
<a href="/react/downloads/react-{{site.react_version}}.zip" class="button">
|
||||
@@ -110,58 +68,12 @@ Then reference it from `helloworld.html`:
|
||||
|
||||
Note that some browsers (Chrome, e.g.) will fail to load the file unless it's served via HTTP.
|
||||
|
||||
### Offline Transform
|
||||
## Using React with npm or Bower
|
||||
|
||||
First install the [Babel](http://babeljs.io/) command-line tools (requires [npm](https://www.npmjs.com/)):
|
||||
|
||||
```
|
||||
npm install --global babel-cli
|
||||
npm install babel-preset-react
|
||||
```
|
||||
|
||||
Then, translate your `src/helloworld.js` file to plain JavaScript:
|
||||
|
||||
```
|
||||
babel --presets react src --watch --out-dir build
|
||||
```
|
||||
|
||||
> Note:
|
||||
>
|
||||
> If you are using ES2015, you will want to also use the `babel-preset-es2015` package.
|
||||
|
||||
The file `build/helloworld.js` is autogenerated whenever you make a change. Read the [Babel CLI documentation](http://babeljs.io/docs/usage/cli/) for more advanced usage.
|
||||
|
||||
```javascript{2}
|
||||
ReactDOM.render(
|
||||
React.createElement('h1', null, 'Hello, world!'),
|
||||
document.getElementById('example')
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
Update your HTML file as below:
|
||||
|
||||
```html{8,12}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Hello React!</title>
|
||||
<script src="build/react.js"></script>
|
||||
<script src="build/react-dom.js"></script>
|
||||
<!-- No need for Babel! -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="example"></div>
|
||||
<script src="build/helloworld.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
You can also use React with package managers like npm or Bower. You can learn more in our [Package Managers](/react/docs/package-management.html) section.
|
||||
|
||||
## Next Steps
|
||||
|
||||
Check out [the tutorial](/react/docs/tutorial.html) and the other examples in the starter kit's `examples` directory to learn more.
|
||||
|
||||
We also have a wiki where the community contributes with [workflows, UI-components, routing, data management etc.](https://github.com/facebook/react/wiki/Complementary-Tools)
|
||||
|
||||
Good luck, and welcome!
|
||||
|
||||
@@ -139,7 +139,7 @@ The `react-dom` package provides DOM-specific methods that can be used at the to
|
||||
### ReactDOM.render
|
||||
|
||||
```javascript
|
||||
ReactComponent render(
|
||||
render(
|
||||
ReactElement element,
|
||||
DOMElement container,
|
||||
[function callback]
|
||||
@@ -161,6 +161,10 @@ If the optional callback is provided, it will be executed after the component is
|
||||
> `ReactDOM.render()` does not modify the container node (only modifies the children of the container). In
|
||||
> the future, it may be possible to insert a component to an existing DOM node without overwriting
|
||||
> the existing children.
|
||||
>
|
||||
> `ReactDOM.render()` currently returns a reference to the root `ReactComponent` instance. However, using this return value is legacy
|
||||
> and should be avoided because future versions of React may render components asynchronously in some cases. If you need a reference to the root `ReactComponent` instance, the preferred solution is to attach a
|
||||
> [callback ref](/react/docs/more-about-refs.html#the-ref-callback-attribute) to the root element.
|
||||
|
||||
|
||||
### ReactDOM.unmountComponentAtNode
|
||||
|
||||
56
docs/docs/ref-09-webcomponents.zh-CN.md
Normal file
56
docs/docs/ref-09-webcomponents.zh-CN.md
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
id: webcomponents-zh-CN
|
||||
title: Web Components
|
||||
permalink: webcomponents-zh-CN.html
|
||||
prev: reconciliation-zh-CN.html
|
||||
next: glossary-zh-CN.html
|
||||
---
|
||||
|
||||
因为React和Web组件是用来解决不同问题的, 试图比较两者将不可避免地导致似是而非的结论, 。Web组件为可复用组件提供了强大的封装, 而React则提供了一个使DOM与你的数据同步的声明式库。两者的目标是互补的;工程师可以混合使用这些技术。作为一个开发者,你可以在你的Web组件中使用React,或者在React中使用Web组件,或者两者一起使用。
|
||||
|
||||
## 在React中使用Web组件
|
||||
|
||||
```javascript
|
||||
class HelloMessage extends React.Component{
|
||||
render() {
|
||||
return <div>Hello <x-search>{this.props.name}</x-search>!</div>;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 注意:
|
||||
>
|
||||
> 两个组件系统的程序模型 (web 组件 与 react 组件) 区别在
|
||||
> web组件通常暴露一个命令式的API (比如 一个 `video` web 组件可能暴露 `play()`
|
||||
> 与 `pause()` 函数)。 因为web 组件是它们属性的声明式函数,
|
||||
> 他们应该可以执行, 但是如果要访问命令式API的一个web组件, 你将需要在组件上附加一个引用
|
||||
> 并与DOM节点直接交互。如果你在使用第三方web组件,
|
||||
> 我们推荐你写一个React组件作为你的web组件的封装。
|
||||
>
|
||||
> 当前,一个由web组件触发的事件可能不能准确地在React渲染树中传递。
|
||||
> 你将需要手动附加event handlers来在你的react组件中处理这些事件。
|
||||
|
||||
|
||||
## 在Web 组件中使用React
|
||||
|
||||
|
||||
```javascript
|
||||
var proto = Object.create(HTMLElement.prototype, {
|
||||
createdCallback: {
|
||||
value: function() {
|
||||
var mountPoint = document.createElement('span');
|
||||
this.createShadowRoot().appendChild(mountPoint);
|
||||
|
||||
var name = this.getAttribute('name');
|
||||
var url = 'https://www.google.com/search?q=' + encodeURIComponent(name);
|
||||
ReactDOM.render(<a href={url}>{name}</a>, mountPoint);
|
||||
}
|
||||
}
|
||||
});
|
||||
document.registerElement('x-search', {prototype: proto});
|
||||
```
|
||||
|
||||
## 完整样例
|
||||
|
||||
若要查看完整样例,请参照[starter kit](/react/downloads.html)中的`webcomponents`例子。
|
||||
|
||||
@@ -134,7 +134,7 @@ You can start seeing how your application will behave: set `filterText` to `"bal
|
||||
|
||||
So far, we've built an app that renders correctly as a function of props and state flowing down the hierarchy. Now it's time to support data flowing the other way: the form components deep in the hierarchy need to update the state in `FilterableProductTable`.
|
||||
|
||||
React makes this data flow explicit to make it easy to understand how your program works, but it does require a little more typing than traditional two-way data binding. React provides an add-on called `ReactLink` to make this pattern as convenient as two-way binding, but for the purpose of this post, we'll keep everything explicit.
|
||||
React makes this data flow explicit to make it easy to understand how your program works, but it does require a little more typing than traditional two-way data binding.
|
||||
|
||||
If you try to type or check the box in the current version of the example, you'll see that React ignores your input. This is intentional, as we've set the `value` prop of the `input` to always be equal to the `state` passed in from `FilterableProductTable`.
|
||||
|
||||
|
||||
@@ -380,8 +380,8 @@ When the component is first created, we want to GET some JSON from the server an
|
||||
|
||||
```json
|
||||
[
|
||||
{"author": "Pete Hunt", "text": "This is one comment"},
|
||||
{"author": "Jordan Walke", "text": "This is *another* comment"}
|
||||
{"id": "1", "author": "Pete Hunt", "text": "This is one comment"},
|
||||
{"id": "2", "author": "Jordan Walke", "text": "This is *another* comment"}
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
BIN
docs/downloads/react-0.14.8.zip
Normal file
BIN
docs/downloads/react-0.14.8.zip
Normal file
Binary file not shown.
BIN
docs/downloads/react-15.0.0.zip
Normal file
BIN
docs/downloads/react-15.0.0.zip
Normal file
Binary file not shown.
BIN
docs/downloads/react-15.0.1.zip
Normal file
BIN
docs/downloads/react-15.0.1.zip
Normal file
Binary file not shown.
4
docs/js/react-dom.js
vendored
4
docs/js/react-dom.js
vendored
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* ReactDOM v0.14.7
|
||||
* ReactDOM v15.0.1
|
||||
*
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
|
||||
8458
docs/js/react.js
vendored
8458
docs/js/react.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -8,8 +8,8 @@
|
||||
"babel-preset-react": "^6.5.0",
|
||||
"babelify": "^7.2.0",
|
||||
"browserify": "^11.0.1",
|
||||
"react": "15.0.0-rc.2",
|
||||
"react-dom": "15.0.0-rc.2",
|
||||
"react": "15.0.1",
|
||||
"react-dom": "15.0.1",
|
||||
"watchify": "^3.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
46
grunt/tasks/npm-react-native.js
Normal file
46
grunt/tasks/npm-react-native.js
Normal file
@@ -0,0 +1,46 @@
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var grunt = require('grunt');
|
||||
|
||||
var src = 'packages/react-native-renderer/';
|
||||
var dest = 'build/packages/react-native-renderer/';
|
||||
|
||||
function buildRelease() {
|
||||
if (grunt.file.exists(dest)) {
|
||||
grunt.file.delete(dest);
|
||||
}
|
||||
|
||||
// Copy to build/packages/react-native-renderer
|
||||
var mappings = [].concat(
|
||||
grunt.file.expandMapping('**/*', dest, {cwd: src}),
|
||||
grunt.file.expandMapping('{LICENSE,PATENTS}', dest)
|
||||
);
|
||||
mappings.forEach(function(mapping) {
|
||||
var mappingSrc = mapping.src[0];
|
||||
var mappingDest = mapping.dest;
|
||||
if (grunt.file.isDir(mappingSrc)) {
|
||||
grunt.file.mkdir(mappingDest);
|
||||
} else {
|
||||
grunt.file.copy(mappingSrc, mappingDest);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function packRelease() {
|
||||
var done = this.async();
|
||||
var spawnCmd = {
|
||||
cmd: 'npm',
|
||||
args: ['pack', 'packages/react-native-renderer'],
|
||||
};
|
||||
grunt.util.spawn(spawnCmd, function() {
|
||||
var buildSrc = 'react-native-renderer-' + grunt.config.data.pkg.version + '.tgz';
|
||||
var buildDest = 'build/packages/react-native-renderer.tgz';
|
||||
fs.rename(buildSrc, buildDest, done);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildRelease: buildRelease,
|
||||
packRelease: packRelease,
|
||||
};
|
||||
@@ -16,7 +16,7 @@ var BOWER_FILES = [
|
||||
];
|
||||
|
||||
var EXAMPLES_PATH = 'examples/';
|
||||
var EXAMPLES_GLOB = [EXAMPLES_PATH + '**/*.*'];
|
||||
var EXAMPLES_GLOB = [EXAMPLES_PATH + '**/*.*', EXAMPLES_PATH + '**/.babelrc'];
|
||||
|
||||
var STARTER_PATH = 'starter/';
|
||||
var STARTER_GLOB = [STARTER_PATH + '/**/*.*'];
|
||||
|
||||
@@ -11,6 +11,8 @@ module.exports = function() {
|
||||
grunt.file.readJSON('./packages/react/package.json').version,
|
||||
'packages/react-dom/package.json':
|
||||
grunt.file.readJSON('./packages/react-dom/package.json').version,
|
||||
'packages/react-native-renderer/package.json':
|
||||
grunt.file.readJSON('./packages/react-native-renderer/package.json').version,
|
||||
'packages/react-addons/package.json (version)': addonsData.version,
|
||||
// Get the "version" without the range bit
|
||||
'packages/react-addons/package.json (react dependency)': addonsData.peerDependencies.react.slice(1),
|
||||
|
||||
38
gulpfile.js
38
gulpfile.js
@@ -20,6 +20,7 @@ var paths = {
|
||||
react: {
|
||||
src: [
|
||||
'src/**/*.js',
|
||||
'!src/**/__benchmarks__/**/*.js',
|
||||
'!src/**/__tests__/**/*.js',
|
||||
'!src/**/__mocks__/**/*.js',
|
||||
'!src/shared/vendor/**/*.js',
|
||||
@@ -28,17 +29,36 @@ var paths = {
|
||||
},
|
||||
};
|
||||
|
||||
var fbjsModuleMap = require('fbjs/module-map');
|
||||
var moduleMap = {};
|
||||
for (var key in fbjsModuleMap) {
|
||||
moduleMap[key] = fbjsModuleMap[key];
|
||||
}
|
||||
var whiteListNames = [
|
||||
'deepDiffer',
|
||||
'deepFreezeAndThrowOnMutationInDev',
|
||||
'flattenStyle',
|
||||
'InitializeJavaScriptAppEngine',
|
||||
'InteractionManager',
|
||||
'JSTimersExecution',
|
||||
'merge',
|
||||
'Platform',
|
||||
'RCTEventEmitter',
|
||||
'RCTLog',
|
||||
'TextInputState',
|
||||
'UIManager',
|
||||
'View',
|
||||
];
|
||||
|
||||
whiteListNames.forEach(function(name) {
|
||||
moduleMap[name] = name;
|
||||
});
|
||||
|
||||
moduleMap['object-assign'] = 'object-assign';
|
||||
|
||||
var babelOpts = {
|
||||
plugins: [
|
||||
[babelPluginModules, {
|
||||
map: Object.assign(
|
||||
{},
|
||||
require('fbjs/module-map'),
|
||||
{
|
||||
'object-assign': 'object-assign',
|
||||
}
|
||||
),
|
||||
}],
|
||||
[babelPluginModules, { map: moduleMap }],
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
15179
npm-shrinkwrap.json
generated
Normal file
15179
npm-shrinkwrap.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-build",
|
||||
"private": true,
|
||||
"version": "15.0.0-rc.2",
|
||||
"version": "15.0.2",
|
||||
"devDependencies": {
|
||||
"async": "^1.5.0",
|
||||
"babel-cli": "^6.6.5",
|
||||
@@ -25,6 +25,7 @@
|
||||
"babel-plugin-transform-es2015-template-literals": "^6.5.2",
|
||||
"babel-plugin-transform-es3-member-expression-literals": "^6.5.0",
|
||||
"babel-plugin-transform-es3-property-literals": "^6.5.0",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.6.5",
|
||||
"babel-preset-react": "^6.5.0",
|
||||
"browserify": "^13.0.0",
|
||||
"bundle-collapser": "^1.1.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-addons-template",
|
||||
"version": "15.0.0-rc.2",
|
||||
"version": "15.0.2",
|
||||
"main": "index.js",
|
||||
"repository": "facebook/react",
|
||||
"keywords": [
|
||||
@@ -10,6 +10,6 @@
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {},
|
||||
"peerDependencies": {
|
||||
"react": "^15.0.0-rc.2"
|
||||
"react": "^15.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-dom",
|
||||
"version": "15.0.0-rc.2",
|
||||
"version": "15.0.2",
|
||||
"description": "React package for working with the DOM.",
|
||||
"main": "index.js",
|
||||
"repository": "facebook/react",
|
||||
@@ -14,6 +14,6 @@
|
||||
"homepage": "https://facebook.github.io/react/",
|
||||
"dependencies": {},
|
||||
"peerDependencies": {
|
||||
"react": "^15.0.0-rc.2"
|
||||
"react": "^15.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,6 @@
|
||||
"author": "",
|
||||
"license": "BSD-3-Clause",
|
||||
"peerDependencies": {
|
||||
"react": "^15.0.0-rc.2"
|
||||
"react": "^15.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
5
packages/react-native-renderer/README.md
Normal file
5
packages/react-native-renderer/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# `react-native-renderer`
|
||||
|
||||
This package is the renderer that is used by the react-native package.
|
||||
It is intended to be used inside the react-native environment. It is not
|
||||
intended to be used stand alone.
|
||||
3
packages/react-native-renderer/index.js
vendored
Normal file
3
packages/react-native-renderer/index.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = require('react/lib/ReactNative');
|
||||
19
packages/react-native-renderer/package.json
Normal file
19
packages/react-native-renderer/package.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "react-native-renderer",
|
||||
"version": "15.0.2",
|
||||
"description": "React package for use inside react-native.",
|
||||
"main": "index.js",
|
||||
"repository": "facebook/react",
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-native"
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"bugs": {
|
||||
"url": "https://github.com/facebook/react/issues"
|
||||
},
|
||||
"homepage": "https://facebook.github.io/react-native/",
|
||||
"dependencies": {
|
||||
"react": "^15.0.2"
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var ReactUpdates = require('./ReactUpdates');
|
||||
|
||||
// TODO: In React Native, ReactTestUtils depends on ./ReactDOM (for
|
||||
// renderIntoDocument, which should never be called) and Relay depends on
|
||||
// react-dom (for batching). Once those are fixed, nothing in RN should import
|
||||
// this module and this file can go away.
|
||||
|
||||
module.exports = {
|
||||
unstable_batchedUpdates: ReactUpdates.batchedUpdates,
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react",
|
||||
"description": "React is a JavaScript library for building user interfaces.",
|
||||
"version": "15.0.0-rc.2",
|
||||
"version": "15.0.2",
|
||||
"keywords": [
|
||||
"react"
|
||||
],
|
||||
|
||||
@@ -12,6 +12,20 @@
|
||||
module.exports = function autoImporter(babel) {
|
||||
const t = babel.types;
|
||||
|
||||
function getAssignIdent(path, file, state) {
|
||||
if (!state.id) {
|
||||
state.id = path.scope.generateUidIdentifier('assign');
|
||||
path.scope.getProgramParent().push({
|
||||
id: state.id,
|
||||
init: t.callExpression(
|
||||
t.identifier('require'),
|
||||
[t.stringLiteral('object-assign')]
|
||||
),
|
||||
});
|
||||
}
|
||||
return state.id;
|
||||
}
|
||||
|
||||
return {
|
||||
pre: function() {
|
||||
// map from module to generated identifier
|
||||
@@ -22,17 +36,15 @@ module.exports = function autoImporter(babel) {
|
||||
CallExpression: function(path, file) {
|
||||
if (path.get('callee').matchesPattern('Object.assign')) {
|
||||
// generate identifier and require if it hasn't been already
|
||||
if (!this.id) {
|
||||
this.id = path.scope.generateUidIdentifier('assign');
|
||||
path.scope.getProgramParent().push({
|
||||
id: this.id,
|
||||
init: t.callExpression(
|
||||
t.identifier('require'),
|
||||
[t.stringLiteral('object-assign')]
|
||||
),
|
||||
});
|
||||
}
|
||||
path.node.callee = this.id;
|
||||
var id = getAssignIdent(path, file, this);
|
||||
path.node.callee = id;
|
||||
}
|
||||
},
|
||||
|
||||
MemberExpression: function(path, file) {
|
||||
if (path.matchesPattern('Object.assign')) {
|
||||
var id = getAssignIdent(path, file, this);
|
||||
path.replaceWith(id);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -11,4 +11,4 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = '15.0.0-rc.2';
|
||||
module.exports = '15.0.2';
|
||||
|
||||
@@ -21,6 +21,7 @@ var ReactPropTypes = require('ReactPropTypes');
|
||||
var ReactVersion = require('ReactVersion');
|
||||
|
||||
var onlyChild = require('onlyChild');
|
||||
var warning = require('warning');
|
||||
|
||||
var createElement = ReactElement.createElement;
|
||||
var createFactory = ReactElement.createFactory;
|
||||
@@ -32,6 +33,23 @@ if (__DEV__) {
|
||||
cloneElement = ReactElementValidator.cloneElement;
|
||||
}
|
||||
|
||||
var __spread = Object.assign;
|
||||
|
||||
if (__DEV__) {
|
||||
var warned = false;
|
||||
__spread = function() {
|
||||
warning(
|
||||
warned,
|
||||
'React.__spread is deprecated and should not be used. Use ' +
|
||||
'Object.assign directly or another helper function with similar ' +
|
||||
'semantics. You may be seeing this warning due to your compiler. ' +
|
||||
'See https://fb.me/react-spread-deprecation for more details.'
|
||||
);
|
||||
warned = true;
|
||||
return Object.assign.apply(null, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
var React = {
|
||||
|
||||
// Modern
|
||||
@@ -65,6 +83,9 @@ var React = {
|
||||
DOM: ReactDOMFactories,
|
||||
|
||||
version: ReactVersion,
|
||||
|
||||
// Deprecated hook for JSX spread, don't use this for anything.
|
||||
__spread: __spread,
|
||||
};
|
||||
|
||||
module.exports = React;
|
||||
|
||||
@@ -146,7 +146,7 @@ function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
|
||||
/**
|
||||
* Maps children that are typically specified as `props.children`.
|
||||
*
|
||||
* The provided mapFunction(child, key, index) will be called for each
|
||||
* The provided mapFunction(child, index) will be called for each
|
||||
* leaf child.
|
||||
*
|
||||
* @param {?*} children Children tree container.
|
||||
|
||||
@@ -22,7 +22,7 @@ var invariant = require('invariant');
|
||||
* of children.
|
||||
*
|
||||
* @param {?object} children Child collection structure.
|
||||
* @return {ReactComponent} The first and only `ReactComponent` contained in the
|
||||
* @return {ReactElement} The first and only `ReactElement` contained in the
|
||||
* structure.
|
||||
*/
|
||||
function onlyChild(children) {
|
||||
|
||||
@@ -1,505 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule OrderedMap
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
var PREFIX = 'key:';
|
||||
|
||||
/**
|
||||
* Utility to extract a backing object from an initialization `Array`, allowing
|
||||
* the caller to assist in resolving the unique ID for each entry via the
|
||||
* `keyExtractor` callback. The `keyExtractor` must extract non-empty strings or
|
||||
* numbers.
|
||||
* @param {Array<Object!>} arr Array of items.
|
||||
* @param {function} keyExtractor Extracts a unique key from each item.
|
||||
* @return {Object} Map from unique key to originating value that the key was
|
||||
* extracted from.
|
||||
* @throws Exception if the initialization array has duplicate extracted keys.
|
||||
*/
|
||||
function extractObjectFromArray(arr, keyExtractor) {
|
||||
var normalizedObj = {};
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
var item = arr[i];
|
||||
var key = keyExtractor(item);
|
||||
assertValidPublicKey(key);
|
||||
var normalizedKey = PREFIX + key;
|
||||
invariant(
|
||||
!(normalizedKey in normalizedObj),
|
||||
'OrderedMap: IDs returned by the key extraction function must be unique.'
|
||||
);
|
||||
normalizedObj[normalizedKey] = item;
|
||||
}
|
||||
return normalizedObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class for mappings with ordering. This class is to be used in an
|
||||
* immutable manner. A `OrderedMap` is very much like the native JavaScript
|
||||
* object, where keys map to values via the `get()` function. Also, like the
|
||||
* native JavaScript object, there is an ordering associated with the mapping.
|
||||
* This class is helpful because it eliminates many of the pitfalls that come
|
||||
* with the native JavaScript ordered mappings. Specifically, there are
|
||||
* inconsistencies with numeric keys in some JavaScript implementations
|
||||
* (enumeration ordering). This class protects against those pitfalls and
|
||||
* provides functional utilities for dealing with these `OrderedMap`s.
|
||||
*
|
||||
* - TODO:
|
||||
* - orderedMergeExclusive: Merges mutually exclusive `OrderedMap`s.
|
||||
* - mapReverse().
|
||||
*
|
||||
* @class {OrderedMap}
|
||||
* @constructor {OrderedMap}
|
||||
* @param {Object} normalizedObj Object that is known to be a defensive copy of
|
||||
* caller supplied data. We require a defensive copy to guard against callers
|
||||
* mutating. It is also assumed that the keys of `normalizedObj` have been
|
||||
* normalized and do not contain any numeric-appearing strings.
|
||||
* @param {number} computedLength The precomputed length of `_normalizedObj`
|
||||
* keys.
|
||||
* @private
|
||||
*/
|
||||
function OrderedMapImpl(normalizedObj, computedLength) {
|
||||
this._normalizedObj = normalizedObj;
|
||||
this._computedPositions = null;
|
||||
this.length = computedLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a "public" key - that is, one that the public facing API supplies.
|
||||
* The key is then normalized for internal storage. In order to be considered
|
||||
* valid, all keys must be non-empty, defined, non-null strings or numbers.
|
||||
*
|
||||
* @param {string?} key Validates that the key is suitable for use in a
|
||||
* `OrderedMap`.
|
||||
* @throws Error if key is not appropriate for use in `OrderedMap`.
|
||||
*/
|
||||
function assertValidPublicKey(key) {
|
||||
invariant(
|
||||
key !== '' && (typeof key === 'string' || typeof key === 'number'),
|
||||
'OrderedMap: Key must be non-empty, non-null string or number.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that arguments to range operations are within the correct limits.
|
||||
*
|
||||
* @param {number} start Start of range.
|
||||
* @param {number} length Length of range.
|
||||
* @param {number} actualLen Actual length of range that should not be
|
||||
* exceeded.
|
||||
* @throws Error if range arguments are out of bounds.
|
||||
*/
|
||||
function assertValidRangeIndices(start, length, actualLen) {
|
||||
invariant(
|
||||
typeof start === 'number' &&
|
||||
typeof length === 'number' &&
|
||||
length >= 0 &&
|
||||
start >= 0 &&
|
||||
start + length <= actualLen,
|
||||
'OrderedMap: `mapRange` and `forEachRange` expect non-negative start and ' +
|
||||
'length arguments within the bounds of the instance.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two "normalized" objects (objects who's key have been normalized) into
|
||||
* a `OrderedMap`.
|
||||
*
|
||||
* @param {Object} a Object of key value pairs.
|
||||
* @param {Object} b Object of key value pairs.
|
||||
* @return {OrderedMap} new `OrderedMap` that results in merging `a` and `b`.
|
||||
*/
|
||||
function _fromNormalizedObjects(a, b) {
|
||||
// Second optional, both must be plain JavaScript objects.
|
||||
invariant(
|
||||
a && a.constructor === Object && (!b || b.constructor === Object),
|
||||
'OrderedMap: Corrupted instance of OrderedMap detected.'
|
||||
);
|
||||
|
||||
var newSet = {};
|
||||
var length = 0;
|
||||
var key;
|
||||
for (key in a) {
|
||||
if (a.hasOwnProperty(key)) {
|
||||
newSet[key] = a[key];
|
||||
length++;
|
||||
}
|
||||
}
|
||||
|
||||
for (key in b) {
|
||||
if (b.hasOwnProperty(key)) {
|
||||
// Increment length if not already added via first object (a)
|
||||
if (!(key in newSet)) {
|
||||
length++;
|
||||
}
|
||||
newSet[key] = b[key];
|
||||
}
|
||||
}
|
||||
return new OrderedMapImpl(newSet, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods for `OrderedMap` instances.
|
||||
*
|
||||
* @lends OrderedMap.prototype
|
||||
* TODO: Make this data structure lazy, unify with LazyArray.
|
||||
* TODO: Unify this with ImmutableObject - it is to be used immutably.
|
||||
* TODO: If so, consider providing `fromObject` API.
|
||||
* TODO: Create faster implementation of merging/mapping from original Array,
|
||||
* without having to first create an object - simply for the sake of merging.
|
||||
*/
|
||||
var OrderedMapMethods = {
|
||||
|
||||
/**
|
||||
* Returns whether or not a given key is present in the map.
|
||||
*
|
||||
* @param {string} key Valid string key to lookup membership for.
|
||||
* @return {boolean} Whether or not `key` is a member of the map.
|
||||
* @throws Error if provided known invalid key.
|
||||
*/
|
||||
has: function(key) {
|
||||
assertValidPublicKey(key);
|
||||
var normalizedKey = PREFIX + key;
|
||||
return normalizedKey in this._normalizedObj;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the object for a given key, or `undefined` if not present. To
|
||||
* distinguish an undefined entry vs not being in the set, use `has()`.
|
||||
*
|
||||
* @param {string} key String key to lookup the value for.
|
||||
* @return {Object?} Object at key `key`, or undefined if not in map.
|
||||
* @throws Error if provided known invalid key.
|
||||
*/
|
||||
get: function(key) {
|
||||
assertValidPublicKey(key);
|
||||
var normalizedKey = PREFIX + key;
|
||||
return this.has(key) ? this._normalizedObj[normalizedKey] : undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* Merges, appending new keys to the end of the ordering. Keys in `orderedMap`
|
||||
* that are redundant with `this`, maintain the same ordering index that they
|
||||
* had in `this`. This is how standard JavaScript object merging would work.
|
||||
* If you wish to prepend a `OrderedMap` to the beginning of another
|
||||
* `OrderedMap` then simply reverse the order of operation. This is the analog
|
||||
* to `merge(x, y)`.
|
||||
*
|
||||
* @param {OrderedMap} orderedMap OrderedMap to merge onto the end.
|
||||
* @return {OrderedMap} New OrderedMap that represents the result of the
|
||||
* merge.
|
||||
*/
|
||||
merge: function(orderedMap) {
|
||||
invariant(
|
||||
orderedMap instanceof OrderedMapImpl,
|
||||
'OrderedMap.merge(...): Expected an OrderedMap instance.'
|
||||
);
|
||||
return _fromNormalizedObjects(
|
||||
this._normalizedObj,
|
||||
orderedMap._normalizedObj
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Functional map API. Returns a new `OrderedMap`.
|
||||
*
|
||||
* @param {Function} cb Callback to invoke for each item.
|
||||
* @param {Object?=} context Context to invoke callback from.
|
||||
* @return {OrderedMap} OrderedMap that results from mapping.
|
||||
*/
|
||||
map: function(cb, context) {
|
||||
return this.mapRange(cb, 0, this.length, context);
|
||||
},
|
||||
|
||||
/**
|
||||
* The callback `cb` is invoked with the arguments (item, key,
|
||||
* indexInOriginal).
|
||||
*
|
||||
* @param {Function} cb Determines result for each item.
|
||||
* @param {number} start Start index of map range.
|
||||
* @param {end} length End index of map range.
|
||||
* @param {*!?} context Context of callback invocation.
|
||||
* @return {OrderedMap} OrderedMap resulting from mapping the range.
|
||||
*/
|
||||
mapRange: function(cb, start, length, context) {
|
||||
var thisSet = this._normalizedObj;
|
||||
var newSet = {};
|
||||
var i = 0;
|
||||
assertValidRangeIndices(start, length, this.length);
|
||||
var end = start + length - 1;
|
||||
for (var key in thisSet) {
|
||||
if (thisSet.hasOwnProperty(key)) {
|
||||
if (i >= start) {
|
||||
if (i > end) {
|
||||
break;
|
||||
}
|
||||
var item = thisSet[key];
|
||||
newSet[key] = cb.call(context, item, key.substr(PREFIX.length), i);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return new OrderedMapImpl(newSet, length);
|
||||
},
|
||||
|
||||
/**
|
||||
* Function filter API. Returns new `OrderedMap`.
|
||||
*
|
||||
* @param {Function} cb Callback to invoke for each item.
|
||||
* @param {Object?=} context Context to invoke callback from.
|
||||
* @return {OrderedMap} OrderedMap that results from filtering.
|
||||
*/
|
||||
filter: function(cb, context) {
|
||||
return this.filterRange(cb, 0, this.length, context);
|
||||
},
|
||||
|
||||
/**
|
||||
* The callback `cb` is invoked with the arguments (item, key,
|
||||
* indexInOriginal).
|
||||
*
|
||||
* @param {Function} cb Returns true if item should be in result.
|
||||
* @param {number} start Start index of filter range.
|
||||
* @param {number} length End index of map range.
|
||||
* @param {*!?} context Context of callback invocation.
|
||||
* @return {OrderedMap} OrderedMap resulting from filtering the range.
|
||||
*/
|
||||
filterRange: function(cb, start, length, context) {
|
||||
var newSet = {};
|
||||
var newSetLength = 0;
|
||||
this.forEachRange(function(item, key, originalIndex) {
|
||||
if (cb.call(context, item, key, originalIndex)) {
|
||||
var normalizedKey = PREFIX + key;
|
||||
newSet[normalizedKey] = item;
|
||||
newSetLength++;
|
||||
}
|
||||
}, start, length);
|
||||
return new OrderedMapImpl(newSet, newSetLength);
|
||||
},
|
||||
|
||||
forEach: function(cb, context) {
|
||||
this.forEachRange(cb, 0, this.length, context);
|
||||
},
|
||||
|
||||
forEachRange: function(cb, start, length, context) {
|
||||
assertValidRangeIndices(start, length, this.length);
|
||||
var thisSet = this._normalizedObj;
|
||||
var i = 0;
|
||||
var end = start + length - 1;
|
||||
for (var key in thisSet) {
|
||||
if (thisSet.hasOwnProperty(key)) {
|
||||
if (i >= start) {
|
||||
if (i > end) {
|
||||
break;
|
||||
}
|
||||
var item = thisSet[key];
|
||||
cb.call(context, item, key.substr(PREFIX.length), i);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Even though `mapRange`/`forEachKeyRange` allow zero length mappings, we'll
|
||||
* impose an additional restriction here that the length of mapping be greater
|
||||
* than zero - the only reason is that there are many ways to express length
|
||||
* zero in terms of two keys and that is confusing.
|
||||
*/
|
||||
mapKeyRange: function(cb, startKey, endKey, context) {
|
||||
var startIndex = this.indexOfKey(startKey);
|
||||
var endIndex = this.indexOfKey(endKey);
|
||||
invariant(
|
||||
startIndex !== undefined && endIndex !== undefined,
|
||||
'mapKeyRange must be given keys that are present.'
|
||||
);
|
||||
invariant(
|
||||
endIndex >= startIndex,
|
||||
'OrderedMap.mapKeyRange(...): `endKey` must not come before `startIndex`.'
|
||||
);
|
||||
return this.mapRange(cb, startIndex, (endIndex - startIndex) + 1, context);
|
||||
},
|
||||
|
||||
forEachKeyRange: function(cb, startKey, endKey, context) {
|
||||
var startIndex = this.indexOfKey(startKey);
|
||||
var endIndex = this.indexOfKey(endKey);
|
||||
invariant(
|
||||
startIndex !== undefined && endIndex !== undefined,
|
||||
'forEachKeyRange must be given keys that are present.'
|
||||
);
|
||||
invariant(
|
||||
endIndex >= startIndex,
|
||||
'OrderedMap.forEachKeyRange(...): `endKey` must not come before ' +
|
||||
'`startIndex`.'
|
||||
);
|
||||
this.forEachRange(cb, startIndex, (endIndex - startIndex) + 1, context);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number} pos Index to search for key at.
|
||||
* @return {string|undefined} Either the key at index `pos` or undefined if
|
||||
* not in map.
|
||||
*/
|
||||
keyAtIndex: function(pos) {
|
||||
var computedPositions = this._getOrComputePositions();
|
||||
var keyAtPos = computedPositions.keyByIndex[pos];
|
||||
return keyAtPos ? keyAtPos.substr(PREFIX.length) : undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} key String key from which to find the next key.
|
||||
* @return {string|undefined} Either the next key, or undefined if there is no
|
||||
* next key.
|
||||
* @throws Error if `key` is not in this `OrderedMap`.
|
||||
*/
|
||||
keyAfter: function(key) {
|
||||
return this.nthKeyAfter(key, 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} key String key from which to find the preceding key.
|
||||
* @return {string|undefined} Either the preceding key, or undefined if there
|
||||
* is no preceding.key.
|
||||
* @throws Error if `key` is not in this `OrderedMap`.
|
||||
*/
|
||||
keyBefore: function(key) {
|
||||
return this.nthKeyBefore(key, 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} key String key from which to find a following key.
|
||||
* @param {number} n Distance to scan forward after `key`.
|
||||
* @return {string|undefined} Either the nth key after `key`, or undefined if
|
||||
* there is no next key.
|
||||
* @throws Error if `key` is not in this `OrderedMap`.
|
||||
*/
|
||||
nthKeyAfter: function(key, n) {
|
||||
var curIndex = this.indexOfKey(key);
|
||||
invariant(
|
||||
curIndex !== undefined,
|
||||
'OrderedMap.nthKeyAfter: The key `%s` does not exist in this instance.',
|
||||
key
|
||||
);
|
||||
return this.keyAtIndex(curIndex + n);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} key String key from which to find a preceding key.
|
||||
* @param {number} n Distance to scan backwards before `key`.
|
||||
* @return {string|undefined} Either the nth key before `key`, or undefined if
|
||||
* there is no previous key.
|
||||
* @throws Error if `key` is not in this `OrderedMap`.
|
||||
*/
|
||||
nthKeyBefore: function(key, n) {
|
||||
return this.nthKeyAfter(key, -n);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} key Key to find the index of.
|
||||
* @return {number|undefined} Index of the provided key, or `undefined` if the
|
||||
* key is not found.
|
||||
*/
|
||||
indexOfKey: function(key) {
|
||||
assertValidPublicKey(key);
|
||||
var normalizedKey = PREFIX + key;
|
||||
var computedPositions = this._getOrComputePositions();
|
||||
var computedPosition = computedPositions.indexByKey[normalizedKey];
|
||||
// Just writing it this way to make it clear this is intentional.
|
||||
return computedPosition === undefined ? undefined : computedPosition;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {Array} An ordered array of this object's values.
|
||||
*/
|
||||
toArray: function() {
|
||||
var result = [];
|
||||
var thisSet = this._normalizedObj;
|
||||
for (var key in thisSet) {
|
||||
if (thisSet.hasOwnProperty(key)) {
|
||||
result.push(thisSet[key]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds the key at a given position, or indicates via `undefined` that that
|
||||
* position does not exist in the `OrderedMap`. It is appropriate to return
|
||||
* undefined, indicating that the key doesn't exist in the `OrderedMap`
|
||||
* because `undefined` is not ever a valid `OrderedMap` key.
|
||||
*
|
||||
* @private
|
||||
* @return {string?} Name of the item at position `pos`, or `undefined` if
|
||||
* there is no item at that position.
|
||||
*/
|
||||
_getOrComputePositions: function() {
|
||||
// TODO: Entertain computing this at construction time in some less
|
||||
// performance critical paths.
|
||||
var computedPositions = this._computedPositions;
|
||||
if (!computedPositions) {
|
||||
this._computePositions();
|
||||
}
|
||||
return this._computedPositions;
|
||||
},
|
||||
|
||||
/**
|
||||
* Precomputes the index/key mapping for future lookup. Since `OrderedMap`s
|
||||
* are immutable, there is only ever a need to perform this once.
|
||||
* @private
|
||||
*/
|
||||
_computePositions: function() {
|
||||
this._computedPositions = {
|
||||
keyByIndex: {},
|
||||
indexByKey: {},
|
||||
};
|
||||
var keyByIndex = this._computedPositions.keyByIndex;
|
||||
var indexByKey = this._computedPositions.indexByKey;
|
||||
var index = 0;
|
||||
var thisSet = this._normalizedObj;
|
||||
for (var key in thisSet) {
|
||||
if (thisSet.hasOwnProperty(key)) {
|
||||
keyByIndex[index] = key;
|
||||
indexByKey[key] = index;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Object.assign(OrderedMapImpl.prototype, OrderedMapMethods);
|
||||
|
||||
var OrderedMap = {
|
||||
from: function(orderedMap) {
|
||||
invariant(
|
||||
orderedMap instanceof OrderedMapImpl,
|
||||
'OrderedMap.from(...): Expected an OrderedMap instance.'
|
||||
);
|
||||
return _fromNormalizedObjects(orderedMap._normalizedObj, null);
|
||||
},
|
||||
|
||||
fromArray: function(arr, keyExtractor) {
|
||||
invariant(
|
||||
Array.isArray(arr),
|
||||
'OrderedMap.fromArray(...): First argument must be an array.'
|
||||
);
|
||||
invariant(
|
||||
typeof keyExtractor === 'function',
|
||||
'OrderedMap.fromArray(...): Second argument must be a function used ' +
|
||||
'to determine the unique key for each entry.'
|
||||
);
|
||||
return new OrderedMapImpl(
|
||||
extractObjectFromArray(arr, keyExtractor),
|
||||
arr.length
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = OrderedMap;
|
||||
@@ -1,107 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactPropTransferer
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var emptyFunction = require('emptyFunction');
|
||||
var joinClasses = require('joinClasses');
|
||||
|
||||
/**
|
||||
* Creates a transfer strategy that will merge prop values using the supplied
|
||||
* `mergeStrategy`. If a prop was previously unset, this just sets it.
|
||||
*
|
||||
* @param {function} mergeStrategy
|
||||
* @return {function}
|
||||
*/
|
||||
function createTransferStrategy(mergeStrategy) {
|
||||
return function(props, key, value) {
|
||||
if (!props.hasOwnProperty(key)) {
|
||||
props[key] = value;
|
||||
} else {
|
||||
props[key] = mergeStrategy(props[key], value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var transferStrategyMerge = createTransferStrategy(function(a, b) {
|
||||
// `merge` overrides the first object's (`props[key]` above) keys using the
|
||||
// second object's (`value`) keys. An object's style's existing `propA` would
|
||||
// get overridden. Flip the order here.
|
||||
return Object.assign({}, b, a);
|
||||
});
|
||||
|
||||
/**
|
||||
* Transfer strategies dictate how props are transferred by `transferPropsTo`.
|
||||
* NOTE: if you add any more exceptions to this list you should be sure to
|
||||
* update `cloneWithProps()` accordingly.
|
||||
*/
|
||||
var TransferStrategies = {
|
||||
/**
|
||||
* Never transfer `children`.
|
||||
*/
|
||||
children: emptyFunction,
|
||||
/**
|
||||
* Transfer the `className` prop by merging them.
|
||||
*/
|
||||
className: createTransferStrategy(joinClasses),
|
||||
/**
|
||||
* Transfer the `style` prop (which is an object) by merging them.
|
||||
*/
|
||||
style: transferStrategyMerge,
|
||||
};
|
||||
|
||||
/**
|
||||
* Mutates the first argument by transferring the properties from the second
|
||||
* argument.
|
||||
*
|
||||
* @param {object} props
|
||||
* @param {object} newProps
|
||||
* @return {object}
|
||||
*/
|
||||
function transferInto(props, newProps) {
|
||||
for (var thisKey in newProps) {
|
||||
if (!newProps.hasOwnProperty(thisKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var transferStrategy = TransferStrategies[thisKey];
|
||||
|
||||
if (transferStrategy && TransferStrategies.hasOwnProperty(thisKey)) {
|
||||
transferStrategy(props, thisKey, newProps[thisKey]);
|
||||
} else if (!props.hasOwnProperty(thisKey)) {
|
||||
props[thisKey] = newProps[thisKey];
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
/**
|
||||
* ReactPropTransferer are capable of transferring props to another component
|
||||
* using a `transferPropsTo` method.
|
||||
*
|
||||
* @class ReactPropTransferer
|
||||
*/
|
||||
var ReactPropTransferer = {
|
||||
|
||||
/**
|
||||
* Merge two props objects using TransferStrategies.
|
||||
*
|
||||
* @param {object} oldProps original props (they take precedence)
|
||||
* @param {object} newProps new props to merge in
|
||||
* @return {object} a new object containing both sets of props merged.
|
||||
*/
|
||||
mergeProps: function(oldProps, newProps) {
|
||||
return transferInto(Object.assign({}, oldProps), newProps);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = ReactPropTransferer;
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "react-haste",
|
||||
"version": "15.0.0-rc.2",
|
||||
"version": "15.0.1",
|
||||
"license": "BSD-3-Clause"
|
||||
}
|
||||
|
||||
@@ -83,5 +83,7 @@ describe('ReactContextValidator', function() {
|
||||
expect(isTagStackValid(['table', 'tr'])).toBe(false);
|
||||
expect(isTagStackValid(['div', 'ul', 'li', 'div', 'li'])).toBe(false);
|
||||
expect(isTagStackValid(['div', 'html'])).toBe(false);
|
||||
expect(isTagStackValid(['body', 'body'])).toBe(false);
|
||||
expect(isTagStackValid(['svg', 'foreignObject', 'body', 'p'])).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -208,6 +208,7 @@ if (__DEV__) {
|
||||
case 'rt':
|
||||
return impliedEndTags.indexOf(parentTag) === -1;
|
||||
|
||||
case 'body':
|
||||
case 'caption':
|
||||
case 'col':
|
||||
case 'colgroup':
|
||||
|
||||
50
src/renderers/dom/client/wrappers/DisabledInputUtils.js
Normal file
50
src/renderers/dom/client/wrappers/DisabledInputUtils.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule DisabledInputUtils
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var disableableMouseListenerNames = {
|
||||
onClick: true,
|
||||
onDoubleClick: true,
|
||||
onMouseDown: true,
|
||||
onMouseMove: true,
|
||||
onMouseUp: true,
|
||||
|
||||
onClickCapture: true,
|
||||
onDoubleClickCapture: true,
|
||||
onMouseDownCapture: true,
|
||||
onMouseMoveCapture: true,
|
||||
onMouseUpCapture: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a native component that does not receive mouse events
|
||||
* when `disabled` is set.
|
||||
*/
|
||||
var DisabledInputUtils = {
|
||||
getNativeProps: function(inst, props) {
|
||||
if (!props.disabled) {
|
||||
return props;
|
||||
}
|
||||
|
||||
// Copy the props, except the mouse listeners
|
||||
var nativeProps = {};
|
||||
for (var key in props) {
|
||||
if (!disableableMouseListenerNames[key] && props.hasOwnProperty(key)) {
|
||||
nativeProps[key] = props[key];
|
||||
}
|
||||
}
|
||||
|
||||
return nativeProps;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = DisabledInputUtils;
|
||||
@@ -11,40 +11,14 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var mouseListenerNames = {
|
||||
onClick: true,
|
||||
onDoubleClick: true,
|
||||
onMouseDown: true,
|
||||
onMouseMove: true,
|
||||
onMouseUp: true,
|
||||
|
||||
onClickCapture: true,
|
||||
onDoubleClickCapture: true,
|
||||
onMouseDownCapture: true,
|
||||
onMouseMoveCapture: true,
|
||||
onMouseUpCapture: true,
|
||||
};
|
||||
var DisabledInputUtils = require('DisabledInputUtils');
|
||||
|
||||
/**
|
||||
* Implements a <button> native component that does not receive mouse events
|
||||
* when `disabled` is set.
|
||||
*/
|
||||
var ReactDOMButton = {
|
||||
getNativeProps: function(inst, props) {
|
||||
if (!props.disabled) {
|
||||
return props;
|
||||
}
|
||||
|
||||
// Copy the props, except the mouse listeners
|
||||
var nativeProps = {};
|
||||
for (var key in props) {
|
||||
if (props.hasOwnProperty(key) && !mouseListenerNames[key]) {
|
||||
nativeProps[key] = props[key];
|
||||
}
|
||||
}
|
||||
|
||||
return nativeProps;
|
||||
},
|
||||
getNativeProps: DisabledInputUtils.getNativeProps,
|
||||
};
|
||||
|
||||
module.exports = ReactDOMButton;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var DisabledInputUtils = require('DisabledInputUtils');
|
||||
var DOMPropertyOperations = require('DOMPropertyOperations');
|
||||
var LinkedValueUtils = require('LinkedValueUtils');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
@@ -72,7 +73,7 @@ var ReactDOMInput = {
|
||||
// Make sure we set .type before any other properties (setting .value
|
||||
// before .type means .value is lost in IE11 and below)
|
||||
type: undefined,
|
||||
}, props, {
|
||||
}, DisabledInputUtils.getNativeProps(inst, props), {
|
||||
defaultChecked: undefined,
|
||||
defaultValue: undefined,
|
||||
value: value != null ? value : inst._wrapperState.initialValue,
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
'use strict';
|
||||
|
||||
var ReactChildren = require('ReactChildren');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
var ReactDOMSelect = require('ReactDOMSelect');
|
||||
|
||||
var warning = require('warning');
|
||||
@@ -32,8 +33,16 @@ var ReactDOMOption = {
|
||||
|
||||
// Look up whether this option is 'selected'
|
||||
var selectValue = null;
|
||||
if (nativeParent != null && nativeParent._tag === 'select') {
|
||||
selectValue = ReactDOMSelect.getSelectValueContext(nativeParent);
|
||||
if (nativeParent != null) {
|
||||
var selectParent = nativeParent;
|
||||
|
||||
if (selectParent._tag === 'optgroup') {
|
||||
selectParent = selectParent._nativeParent;
|
||||
}
|
||||
|
||||
if (selectParent != null && selectParent._tag === 'select') {
|
||||
selectValue = ReactDOMSelect.getSelectValueContext(selectParent);
|
||||
}
|
||||
}
|
||||
|
||||
// If the value is null (e.g., no specified value or after initial mount)
|
||||
@@ -57,6 +66,15 @@ var ReactDOMOption = {
|
||||
inst._wrapperState = {selected: selected};
|
||||
},
|
||||
|
||||
postMountWrapper: function(inst) {
|
||||
// value="" should make a value attribute (#6219)
|
||||
var props = inst._currentElement.props;
|
||||
if (props.value != null) {
|
||||
var node = ReactDOMComponentTree.getNodeFromInstance(inst);
|
||||
node.setAttribute('value', props.value);
|
||||
}
|
||||
},
|
||||
|
||||
getNativeProps: function(inst, props) {
|
||||
var nativeProps = Object.assign({selected: undefined, children: undefined}, props);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var DisabledInputUtils = require('DisabledInputUtils');
|
||||
var LinkedValueUtils = require('LinkedValueUtils');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
@@ -158,7 +159,7 @@ function updateOptions(inst, multiple, propValue) {
|
||||
*/
|
||||
var ReactDOMSelect = {
|
||||
getNativeProps: function(inst, props) {
|
||||
return Object.assign({}, props, {
|
||||
return Object.assign({}, DisabledInputUtils.getNativeProps(inst, props), {
|
||||
onChange: inst._wrapperState.onChange,
|
||||
value: undefined,
|
||||
});
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var DisabledInputUtils = require('DisabledInputUtils');
|
||||
var DOMPropertyOperations = require('DOMPropertyOperations');
|
||||
var LinkedValueUtils = require('LinkedValueUtils');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
@@ -67,7 +68,7 @@ var ReactDOMTextarea = {
|
||||
|
||||
// Always set children to the same thing. In IE9, the selection range will
|
||||
// get reset if `textContent` is mutated.
|
||||
var nativeProps = Object.assign({}, props, {
|
||||
var nativeProps = Object.assign({}, DisabledInputUtils.getNativeProps(inst, props), {
|
||||
defaultValue: undefined,
|
||||
value: undefined,
|
||||
children: inst._wrapperState.initialValue,
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
|
||||
describe('DisabledInputUtils', function() {
|
||||
var React;
|
||||
var ReactDOM;
|
||||
var ReactTestUtils;
|
||||
|
||||
var elements = ['button', 'input', 'select', 'textarea'];
|
||||
|
||||
function expectClickThru(element) {
|
||||
onClick.mockClear();
|
||||
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(element));
|
||||
expect(onClick.mock.calls.length).toBe(1);
|
||||
}
|
||||
|
||||
function expectNoClickThru(element) {
|
||||
onClick.mockClear();
|
||||
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(element));
|
||||
expect(onClick.mock.calls.length).toBe(0);
|
||||
}
|
||||
|
||||
function mounted(element) {
|
||||
element = ReactTestUtils.renderIntoDocument(element);
|
||||
return element;
|
||||
}
|
||||
|
||||
var onClick = jest.genMockFn();
|
||||
|
||||
elements.forEach(function(tagName) {
|
||||
|
||||
describe(tagName, function() {
|
||||
|
||||
beforeEach(function() {
|
||||
React = require('React');
|
||||
ReactDOM = require('ReactDOM');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
});
|
||||
|
||||
it('should forward clicks when it starts out not disabled', function() {
|
||||
var element = React.createElement(tagName, {
|
||||
onClick: onClick,
|
||||
});
|
||||
|
||||
expectClickThru(mounted(element));
|
||||
});
|
||||
|
||||
it('should not forward clicks when it starts out disabled', function() {
|
||||
var element = React.createElement(tagName, {
|
||||
onClick: onClick,
|
||||
disabled: true,
|
||||
});
|
||||
|
||||
expectNoClickThru(mounted(element));
|
||||
});
|
||||
|
||||
it('should forward clicks when it becomes not disabled', function() {
|
||||
var container = document.createElement('div');
|
||||
var element = ReactDOM.render(
|
||||
React.createElement(tagName, { onClick: onClick, disabled: true }),
|
||||
container
|
||||
);
|
||||
element = ReactDOM.render(
|
||||
React.createElement(tagName, { onClick: onClick }),
|
||||
container
|
||||
);
|
||||
expectClickThru(element);
|
||||
});
|
||||
|
||||
it('should not forward clicks when it becomes disabled', function() {
|
||||
var container = document.createElement('div');
|
||||
var element = ReactDOM.render(
|
||||
React.createElement(tagName, { onClick: onClick }),
|
||||
container
|
||||
);
|
||||
element = ReactDOM.render(
|
||||
React.createElement(tagName, { onClick: onClick, disabled: true }),
|
||||
container
|
||||
);
|
||||
expectNoClickThru(element);
|
||||
});
|
||||
|
||||
it('should work correctly if the listener is changed', function() {
|
||||
var container = document.createElement('div');
|
||||
var element = ReactDOM.render(
|
||||
React.createElement(tagName, { onClick: onClick, disabled: true }),
|
||||
container
|
||||
);
|
||||
element = ReactDOM.render(
|
||||
React.createElement(tagName, { onClick: onClick, disabled: false }),
|
||||
container
|
||||
);
|
||||
expectClickThru(element);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,93 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
|
||||
describe('ReactDOMButton', function() {
|
||||
var React;
|
||||
var ReactDOM;
|
||||
var ReactTestUtils;
|
||||
|
||||
var onClick = jest.genMockFn();
|
||||
|
||||
function expectClickThru(button) {
|
||||
onClick.mockClear();
|
||||
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(button));
|
||||
expect(onClick.mock.calls.length).toBe(1);
|
||||
}
|
||||
|
||||
function expectNoClickThru(button) {
|
||||
onClick.mockClear();
|
||||
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(button));
|
||||
expect(onClick.mock.calls.length).toBe(0);
|
||||
}
|
||||
|
||||
function mounted(button) {
|
||||
button = ReactTestUtils.renderIntoDocument(button);
|
||||
return button;
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
React = require('React');
|
||||
ReactDOM = require('ReactDOM');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
});
|
||||
|
||||
it('should forward clicks when it starts out not disabled', function() {
|
||||
expectClickThru(mounted(<button onClick={onClick} />));
|
||||
});
|
||||
|
||||
it('should not forward clicks when it starts out disabled', function() {
|
||||
expectNoClickThru(
|
||||
mounted(<button disabled={true} onClick={onClick} />)
|
||||
);
|
||||
});
|
||||
|
||||
it('should forward clicks when it becomes not disabled', function() {
|
||||
var container = document.createElement('div');
|
||||
var btn = ReactDOM.render(
|
||||
<button disabled={true} onClick={onClick} />,
|
||||
container
|
||||
);
|
||||
btn = ReactDOM.render(
|
||||
<button onClick={onClick} />,
|
||||
container
|
||||
);
|
||||
expectClickThru(btn);
|
||||
});
|
||||
|
||||
it('should not forward clicks when it becomes disabled', function() {
|
||||
var container = document.createElement('div');
|
||||
var btn = ReactDOM.render(
|
||||
<button onClick={onClick} />,
|
||||
container
|
||||
);
|
||||
btn = ReactDOM.render(
|
||||
<button disabled={true} onClick={onClick} />,
|
||||
container
|
||||
);
|
||||
expectNoClickThru(btn);
|
||||
});
|
||||
|
||||
it('should work correctly if the listener is changed', function() {
|
||||
var container = document.createElement('div');
|
||||
var btn = ReactDOM.render(
|
||||
<button disabled={true} onClick={function() {}} />,
|
||||
container
|
||||
);
|
||||
btn = ReactDOM.render(
|
||||
<button disabled={false} onClick={onClick} />,
|
||||
container
|
||||
);
|
||||
expectClickThru(btn);
|
||||
});
|
||||
});
|
||||
@@ -71,4 +71,15 @@ describe('ReactDOMOption', function() {
|
||||
var node = ReactDOM.findDOMNode(stub);
|
||||
expect(node.innerHTML).toBe('foobar');
|
||||
});
|
||||
|
||||
it('should set attribute for empty value', function() {
|
||||
var container = document.createElement('div');
|
||||
var option = ReactDOM.render(<option value="" />, container);
|
||||
expect(option.hasAttribute('value')).toBe(true);
|
||||
expect(option.getAttribute('value')).toBe('');
|
||||
|
||||
ReactDOM.render(<option value="lava" />, container);
|
||||
expect(option.hasAttribute('value')).toBe(true);
|
||||
expect(option.getAttribute('value')).toBe('lava');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -537,4 +537,21 @@ describe('ReactDOMSelect', function() {
|
||||
"Cannot set property 'pendingUpdate' of null"
|
||||
);
|
||||
});
|
||||
|
||||
it('should select grandchild options nested inside an optgroup', function() {
|
||||
var stub =
|
||||
<select value="b" onChange={noop}>
|
||||
<optgroup label="group">
|
||||
<option value="a">a</option>
|
||||
<option value="b">b</option>
|
||||
<option value="c">c</option>
|
||||
</optgroup>
|
||||
</select>;
|
||||
var container = document.createElement('div');
|
||||
var node = ReactDOM.render(stub, container);
|
||||
|
||||
expect(node.options[0].selected).toBe(false); // a
|
||||
expect(node.options[1].selected).toBe(true); // b
|
||||
expect(node.options[2].selected).toBe(false); // c
|
||||
});
|
||||
});
|
||||
|
||||
@@ -149,10 +149,8 @@ var DOMPropertyOperations = {
|
||||
var propName = propertyInfo.propertyName;
|
||||
// Must explicitly cast values for HAS_SIDE_EFFECTS-properties to the
|
||||
// property type before comparing; only `value` does and is string.
|
||||
// Must set `value` property if it is not null and not yet set.
|
||||
if (!propertyInfo.hasSideEffects ||
|
||||
('' + node[propName]) !== ('' + value) ||
|
||||
!node.hasAttribute(propertyInfo.attributeName)) {
|
||||
('' + node[propName]) !== ('' + value)) {
|
||||
// Contrary to `setAttribute`, object properties are properly
|
||||
// `toString`ed by IE8/9.
|
||||
node[propName] = value;
|
||||
|
||||
@@ -60,6 +60,9 @@ var RESERVED_PROPS = {
|
||||
suppressContentEditableWarning: null,
|
||||
};
|
||||
|
||||
// Node type for document fragments (Node.DOCUMENT_FRAGMENT_NODE).
|
||||
var DOC_FRAGMENT_TYPE = 11;
|
||||
|
||||
|
||||
function getDeclarationErrorAddendum(internalInstance) {
|
||||
if (internalInstance) {
|
||||
@@ -213,7 +216,8 @@ function enqueuePutListener(inst, registrationName, listener, transaction) {
|
||||
);
|
||||
}
|
||||
var containerInfo = inst._nativeContainerInfo;
|
||||
var doc = containerInfo._ownerDocument;
|
||||
var isDocumentFragment = containerInfo._node && containerInfo._node.nodeType === DOC_FRAGMENT_TYPE;
|
||||
var doc = isDocumentFragment ? containerInfo._node : containerInfo._ownerDocument;
|
||||
if (!doc) {
|
||||
// Server rendering.
|
||||
return;
|
||||
@@ -235,6 +239,11 @@ function putListener() {
|
||||
);
|
||||
}
|
||||
|
||||
function optionPostMount() {
|
||||
var inst = this;
|
||||
ReactDOMOption.postMountWrapper(inst);
|
||||
}
|
||||
|
||||
// There are so many media events, it makes sense to just
|
||||
// maintain a list rather than create a `trapBubbledEvent` for each
|
||||
var mediaEvents = {
|
||||
@@ -600,6 +609,11 @@ ReactDOMComponent.Mixin = {
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'option':
|
||||
transaction.getReactMountReady().enqueue(
|
||||
optionPostMount,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
return mountImage;
|
||||
|
||||
@@ -22,6 +22,7 @@ function ReactDOMContainerInfo(topLevelWrapper, node) {
|
||||
_ownerDocument: node ?
|
||||
node.nodeType === DOC_NODE_TYPE ? node : node.ownerDocument :
|
||||
null,
|
||||
_node: node,
|
||||
_tag: node ? node.nodeName.toLowerCase() : null,
|
||||
_namespaceURI: node ? node.namespaceURI : null,
|
||||
};
|
||||
|
||||
@@ -290,7 +290,7 @@ var SVGDOMPropertyConfig = {
|
||||
DOMAttributeNames: {},
|
||||
};
|
||||
|
||||
Object.keys(ATTRS).map((key) => {
|
||||
Object.keys(ATTRS).forEach((key) => {
|
||||
SVGDOMPropertyConfig.Properties[key] = 0;
|
||||
if (ATTRS[key]) {
|
||||
SVGDOMPropertyConfig.DOMAttributeNames[key] = ATTRS[key];
|
||||
|
||||
@@ -15,13 +15,11 @@
|
||||
describe('ReactDOMComponent', function() {
|
||||
var React;
|
||||
var ReactDOM;
|
||||
var ReactDOMFeatureFlags;
|
||||
var ReactDOMServer;
|
||||
|
||||
beforeEach(function() {
|
||||
jest.resetModuleRegistry();
|
||||
React = require('React');
|
||||
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags')
|
||||
ReactDOM = require('ReactDOM');
|
||||
ReactDOMServer = require('ReactDOMServer');
|
||||
});
|
||||
@@ -503,30 +501,14 @@ describe('ReactDOMComponent', function() {
|
||||
expect(nodeValueSetter.mock.calls.length).toBe(expected);
|
||||
}
|
||||
|
||||
if (ReactDOMFeatureFlags.useCreateElement) {
|
||||
renderWithValueAndExpect(undefined, 0);
|
||||
renderWithValueAndExpect('', 1);
|
||||
renderWithValueAndExpect('foo', 2);
|
||||
renderWithValueAndExpect('foo', 2);
|
||||
renderWithValueAndExpect(undefined, 3);
|
||||
renderWithValueAndExpect(null, 3);
|
||||
renderWithValueAndExpect('', 4);
|
||||
renderWithValueAndExpect(undefined, 4);
|
||||
} else {
|
||||
renderWithValueAndExpect(undefined, 0);
|
||||
// This differs because we will have created a node with the value
|
||||
// attribute set. This means it will hasAttribute, so we won't try to
|
||||
// set the value.
|
||||
renderWithValueAndExpect('', 0);
|
||||
renderWithValueAndExpect('foo', 1);
|
||||
renderWithValueAndExpect('foo', 1);
|
||||
renderWithValueAndExpect(undefined, 2);
|
||||
renderWithValueAndExpect(null, 2);
|
||||
// Again, much like the initial update case, we will always have the
|
||||
// attribute set so we won't set the value.
|
||||
renderWithValueAndExpect('', 2);
|
||||
renderWithValueAndExpect(undefined, 2);
|
||||
}
|
||||
renderWithValueAndExpect(undefined, 0);
|
||||
renderWithValueAndExpect('', 0);
|
||||
renderWithValueAndExpect('foo', 1);
|
||||
renderWithValueAndExpect('foo', 1);
|
||||
renderWithValueAndExpect(undefined, 2);
|
||||
renderWithValueAndExpect(null, 2);
|
||||
renderWithValueAndExpect('', 2);
|
||||
renderWithValueAndExpect(undefined, 2);
|
||||
});
|
||||
|
||||
it('should not incur unnecessary DOM mutations for boolean properties', function() {
|
||||
|
||||
19
src/renderers/native/ReactIOS/IOSDefaultEventPluginOrder.js
Normal file
19
src/renderers/native/ReactIOS/IOSDefaultEventPluginOrder.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule IOSDefaultEventPluginOrder
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var IOSDefaultEventPluginOrder = [
|
||||
'ResponderEventPlugin',
|
||||
'IOSNativeBridgeEventPlugin',
|
||||
];
|
||||
|
||||
module.exports = IOSDefaultEventPluginOrder;
|
||||
71
src/renderers/native/ReactIOS/IOSNativeBridgeEventPlugin.js
Normal file
71
src/renderers/native/ReactIOS/IOSNativeBridgeEventPlugin.js
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule IOSNativeBridgeEventPlugin
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var EventPropagators = require('EventPropagators');
|
||||
var SyntheticEvent = require('SyntheticEvent');
|
||||
var UIManager = require('UIManager');
|
||||
|
||||
var merge = require('merge');
|
||||
var warning = require('warning');
|
||||
|
||||
var customBubblingEventTypes = UIManager.customBubblingEventTypes;
|
||||
var customDirectEventTypes = UIManager.customDirectEventTypes;
|
||||
|
||||
var allTypesByEventName = {};
|
||||
|
||||
for (var bubblingTypeName in customBubblingEventTypes) {
|
||||
allTypesByEventName[bubblingTypeName] = customBubblingEventTypes[bubblingTypeName];
|
||||
}
|
||||
|
||||
for (var directTypeName in customDirectEventTypes) {
|
||||
warning(
|
||||
!customBubblingEventTypes[directTypeName],
|
||||
'Event cannot be both direct and bubbling: %s',
|
||||
directTypeName
|
||||
);
|
||||
allTypesByEventName[directTypeName] = customDirectEventTypes[directTypeName];
|
||||
}
|
||||
|
||||
var IOSNativeBridgeEventPlugin = {
|
||||
|
||||
eventTypes: merge(customBubblingEventTypes, customDirectEventTypes),
|
||||
|
||||
/**
|
||||
* @see {EventPluginHub.extractEvents}
|
||||
*/
|
||||
extractEvents: function(
|
||||
topLevelType,
|
||||
targetInst,
|
||||
nativeEvent,
|
||||
nativeEventTarget
|
||||
): ?Object {
|
||||
var bubbleDispatchConfig = customBubblingEventTypes[topLevelType];
|
||||
var directDispatchConfig = customDirectEventTypes[topLevelType];
|
||||
var event = SyntheticEvent.getPooled(
|
||||
bubbleDispatchConfig || directDispatchConfig,
|
||||
targetInst,
|
||||
nativeEvent,
|
||||
nativeEventTarget
|
||||
);
|
||||
if (bubbleDispatchConfig) {
|
||||
EventPropagators.accumulateTwoPhaseDispatches(event);
|
||||
} else if (directDispatchConfig) {
|
||||
EventPropagators.accumulateDirectDispatches(event);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return event;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = IOSNativeBridgeEventPlugin;
|
||||
219
src/renderers/native/ReactIOS/NativeMethodsMixin.js
Normal file
219
src/renderers/native/ReactIOS/NativeMethodsMixin.js
Normal file
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule NativeMethodsMixin
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var ReactNativeAttributePayload = require('ReactNativeAttributePayload');
|
||||
var TextInputState = require('TextInputState');
|
||||
var UIManager = require('UIManager');
|
||||
|
||||
var findNodeHandle = require('findNodeHandle');
|
||||
var invariant = require('invariant');
|
||||
|
||||
type MeasureOnSuccessCallback = (
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
pageX: number,
|
||||
pageY: number
|
||||
) => void
|
||||
|
||||
type MeasureInWindowOnSuccessCallback = (
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
) => void
|
||||
|
||||
type MeasureLayoutOnSuccessCallback = (
|
||||
left: number,
|
||||
top: number,
|
||||
width: number,
|
||||
height: number
|
||||
) => void
|
||||
|
||||
function warnForStyleProps(props, validAttributes) {
|
||||
for (var key in validAttributes.style) {
|
||||
if (!(validAttributes[key] || props[key] === undefined)) {
|
||||
console.error(
|
||||
'You are setting the style `{ ' + key + ': ... }` as a prop. You ' +
|
||||
'should nest it in a style object. ' +
|
||||
'E.g. `{ style: { ' + key + ': ... } }`'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `NativeMethodsMixin` provides methods to access the underlying native
|
||||
* component directly. This can be useful in cases when you want to focus
|
||||
* a view or measure its on-screen dimensions, for example.
|
||||
*
|
||||
* The methods described here are available on most of the default components
|
||||
* provided by React Native. Note, however, that they are *not* available on
|
||||
* composite components that aren't directly backed by a native view. This will
|
||||
* generally include most components that you define in your own app. For more
|
||||
* information, see [Direct
|
||||
* Manipulation](docs/direct-manipulation.html).
|
||||
*/
|
||||
var NativeMethodsMixin = {
|
||||
/**
|
||||
* Determines the location on screen, width, and height of the given view and
|
||||
* returns the values via an async callback. If successful, the callback will
|
||||
* be called with the following arguments:
|
||||
*
|
||||
* - x
|
||||
* - y
|
||||
* - width
|
||||
* - height
|
||||
* - pageX
|
||||
* - pageY
|
||||
*
|
||||
* Note that these measurements are not available until after the rendering
|
||||
* has been completed in native. If you need the measurements as soon as
|
||||
* possible, consider using the [`onLayout`
|
||||
* prop](docs/view.html#onlayout) instead.
|
||||
*/
|
||||
measure: function(callback: MeasureOnSuccessCallback) {
|
||||
UIManager.measure(
|
||||
findNodeHandle(this),
|
||||
mountSafeCallback(this, callback)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines the location of the given view in the window and returns the
|
||||
* values via an async callback. If the React root view is embedded in
|
||||
* another native view, this will give you the absolute coordinates. If
|
||||
* successful, the callback will be called with the following
|
||||
* arguments:
|
||||
*
|
||||
* - x
|
||||
* - y
|
||||
* - width
|
||||
* - height
|
||||
*
|
||||
* Note that these measurements are not available until after the rendering
|
||||
* has been completed in native.
|
||||
*/
|
||||
measureInWindow: function(callback: MeasureInWindowOnSuccessCallback) {
|
||||
UIManager.measureInWindow(
|
||||
findNodeHandle(this),
|
||||
mountSafeCallback(this, callback)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Like [`measure()`](#measure), but measures the view relative an ancestor,
|
||||
* specified as `relativeToNativeNode`. This means that the returned x, y
|
||||
* are relative to the origin x, y of the ancestor view.
|
||||
*
|
||||
* As always, to obtain a native node handle for a component, you can use
|
||||
* `React.findNodeHandle(component)`.
|
||||
*/
|
||||
measureLayout: function(
|
||||
relativeToNativeNode: number,
|
||||
onSuccess: MeasureLayoutOnSuccessCallback,
|
||||
onFail: () => void /* currently unused */
|
||||
) {
|
||||
UIManager.measureLayout(
|
||||
findNodeHandle(this),
|
||||
relativeToNativeNode,
|
||||
mountSafeCallback(this, onFail),
|
||||
mountSafeCallback(this, onSuccess)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* This function sends props straight to native. They will not participate in
|
||||
* future diff process - this means that if you do not include them in the
|
||||
* next render, they will remain active (see [Direct
|
||||
* Manipulation](docs/direct-manipulation.html)).
|
||||
*/
|
||||
setNativeProps: function(nativeProps: Object) {
|
||||
if (__DEV__) {
|
||||
warnForStyleProps(nativeProps, this.viewConfig.validAttributes);
|
||||
}
|
||||
|
||||
var updatePayload = ReactNativeAttributePayload.create(
|
||||
nativeProps,
|
||||
this.viewConfig.validAttributes
|
||||
);
|
||||
|
||||
UIManager.updateView(
|
||||
findNodeHandle(this),
|
||||
this.viewConfig.uiViewClassName,
|
||||
updatePayload
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Requests focus for the given input or view. The exact behavior triggered
|
||||
* will depend on the platform and type of view.
|
||||
*/
|
||||
focus: function() {
|
||||
TextInputState.focusTextInput(findNodeHandle(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes focus from an input or view. This is the opposite of `focus()`.
|
||||
*/
|
||||
blur: function() {
|
||||
TextInputState.blurTextInput(findNodeHandle(this));
|
||||
},
|
||||
};
|
||||
|
||||
function throwOnStylesProp(component, props) {
|
||||
if (props.styles !== undefined) {
|
||||
var owner = component._owner || null;
|
||||
var name = component.constructor.displayName;
|
||||
var msg = '`styles` is not a supported property of `' + name + '`, did ' +
|
||||
'you mean `style` (singular)?';
|
||||
if (owner && owner.constructor && owner.constructor.displayName) {
|
||||
msg += '\n\nCheck the `' + owner.constructor.displayName + '` parent ' +
|
||||
' component.';
|
||||
}
|
||||
throw new Error(msg);
|
||||
}
|
||||
}
|
||||
if (__DEV__) {
|
||||
// hide this from Flow since we can't define these properties outside of
|
||||
// __DEV__ without actually implementing them (setting them to undefined
|
||||
// isn't allowed by ReactClass)
|
||||
var NativeMethodsMixin_DEV = (NativeMethodsMixin: any);
|
||||
invariant(
|
||||
!NativeMethodsMixin_DEV.componentWillMount &&
|
||||
!NativeMethodsMixin_DEV.componentWillReceiveProps,
|
||||
'Do not override existing functions.'
|
||||
);
|
||||
NativeMethodsMixin_DEV.componentWillMount = function() {
|
||||
throwOnStylesProp(this, this.props);
|
||||
};
|
||||
NativeMethodsMixin_DEV.componentWillReceiveProps = function(newProps) {
|
||||
throwOnStylesProp(this, newProps);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* In the future, we should cleanup callbacks by cancelling them instead of
|
||||
* using this.
|
||||
*/
|
||||
var mountSafeCallback = function(context: ReactComponent, callback: ?Function): any {
|
||||
return function() {
|
||||
if (!callback || (context.isMounted && !context.isMounted())) {
|
||||
return undefined;
|
||||
}
|
||||
return callback.apply(context, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = NativeMethodsMixin;
|
||||
76
src/renderers/native/ReactNative/ReactNative.js
Normal file
76
src/renderers/native/ReactNative/ReactNative.js
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNative
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
// Require ReactNativeDefaultInjection first for its side effects of setting up
|
||||
// the JS environment
|
||||
var ReactNativeComponentTree = require('ReactNativeComponentTree');
|
||||
var ReactNativeDefaultInjection = require('ReactNativeDefaultInjection');
|
||||
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactNativeMount = require('ReactNativeMount');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
|
||||
var findNodeHandle = require('findNodeHandle');
|
||||
|
||||
ReactNativeDefaultInjection.inject();
|
||||
|
||||
var render = function(
|
||||
element: ReactElement,
|
||||
mountInto: number,
|
||||
callback?: ?(() => void)
|
||||
): ?ReactComponent {
|
||||
return ReactNativeMount.renderComponent(element, mountInto, callback);
|
||||
};
|
||||
|
||||
var ReactNative = {
|
||||
hasReactNativeInitialized: false,
|
||||
findNodeHandle: findNodeHandle,
|
||||
render: render,
|
||||
unmountComponentAtNode: ReactNativeMount.unmountComponentAtNode,
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
unstable_batchedUpdates: ReactUpdates.batchedUpdates,
|
||||
/* eslint-enable camelcase */
|
||||
|
||||
unmountComponentAtNodeAndRemoveContainer: ReactNativeMount.unmountComponentAtNodeAndRemoveContainer,
|
||||
};
|
||||
|
||||
// Inject the runtime into a devtools global hook regardless of browser.
|
||||
// Allows for debugging when the hook is injected on the page.
|
||||
/* globals __REACT_DEVTOOLS_GLOBAL_HOOK__ */
|
||||
if (
|
||||
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
|
||||
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function') {
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({
|
||||
ComponentTree: {
|
||||
getClosestInstanceFromNode: function(node) {
|
||||
return ReactNativeComponentTree.getClosestInstanceFromNode(node);
|
||||
},
|
||||
getNodeFromInstance: function(inst) {
|
||||
// inst is an internal instance (but could be a composite)
|
||||
while (inst._renderedComponent) {
|
||||
inst = inst._renderedComponent;
|
||||
}
|
||||
if (inst) {
|
||||
return ReactNativeComponentTree.getNodeFromInstance(inst);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
},
|
||||
Mount: ReactNativeMount,
|
||||
Reconciler: require('ReactReconciler'),
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = ReactNative;
|
||||
546
src/renderers/native/ReactNative/ReactNativeAttributePayload.js
Normal file
546
src/renderers/native/ReactNative/ReactNativeAttributePayload.js
Normal file
@@ -0,0 +1,546 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNativeAttributePayload
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var Platform = require('Platform');
|
||||
var ReactNativePropRegistry = require('ReactNativePropRegistry');
|
||||
|
||||
var deepDiffer = require('deepDiffer');
|
||||
var flattenStyle = require('flattenStyle');
|
||||
|
||||
var emptyObject = {};
|
||||
|
||||
/**
|
||||
* Create a payload that contains all the updates between two sets of props.
|
||||
*
|
||||
* These helpers are all encapsulated into a single module, because they use
|
||||
* mutation as a performance optimization which leads to subtle shared
|
||||
* dependencies between the code paths. To avoid this mutable state leaking
|
||||
* across modules, I've kept them isolated to this module.
|
||||
*/
|
||||
|
||||
type AttributeDiffer = (prevProp: mixed, nextProp: mixed) => boolean;
|
||||
type AttributePreprocessor = (nextProp: mixed) => mixed;
|
||||
|
||||
type CustomAttributeConfiguration =
|
||||
{ diff: AttributeDiffer, process: AttributePreprocessor } |
|
||||
{ diff: AttributeDiffer } |
|
||||
{ process: AttributePreprocessor };
|
||||
|
||||
type AttributeConfiguration =
|
||||
{ [key: string]: (
|
||||
CustomAttributeConfiguration | AttributeConfiguration /*| boolean*/
|
||||
) };
|
||||
|
||||
type NestedNode = Array<NestedNode> | Object | number;
|
||||
|
||||
// Tracks removed keys
|
||||
var removedKeys = null;
|
||||
var removedKeyCount = 0;
|
||||
|
||||
function translateKey(propKey: string) : string {
|
||||
if (propKey === 'transform') {
|
||||
// We currently special case the key for `transform`. iOS uses the
|
||||
// transformMatrix name and Android uses the decomposedMatrix name.
|
||||
// TODO: We could unify these names and just use the name `transform`
|
||||
// all the time. Just need to update the native side.
|
||||
if (Platform.OS === 'android') {
|
||||
return 'decomposedMatrix';
|
||||
} else {
|
||||
return 'transformMatrix';
|
||||
}
|
||||
}
|
||||
return propKey;
|
||||
}
|
||||
|
||||
function defaultDiffer(prevProp: mixed, nextProp: mixed) : boolean {
|
||||
if (typeof nextProp !== 'object' || nextProp === null) {
|
||||
// Scalars have already been checked for equality
|
||||
return true;
|
||||
} else {
|
||||
// For objects and arrays, the default diffing algorithm is a deep compare
|
||||
return deepDiffer(prevProp, nextProp);
|
||||
}
|
||||
}
|
||||
|
||||
function resolveObject(idOrObject: number | Object) : Object {
|
||||
if (typeof idOrObject === 'number') {
|
||||
return ReactNativePropRegistry.getByID(idOrObject);
|
||||
}
|
||||
return idOrObject;
|
||||
}
|
||||
|
||||
function restoreDeletedValuesInNestedArray(
|
||||
updatePayload: Object,
|
||||
node: NestedNode,
|
||||
validAttributes: AttributeConfiguration
|
||||
) {
|
||||
if (Array.isArray(node)) {
|
||||
var i = node.length;
|
||||
while (i-- && removedKeyCount > 0) {
|
||||
restoreDeletedValuesInNestedArray(
|
||||
updatePayload,
|
||||
node[i],
|
||||
validAttributes
|
||||
);
|
||||
}
|
||||
} else if (node && removedKeyCount > 0) {
|
||||
var obj = resolveObject(node);
|
||||
for (var propKey in removedKeys) {
|
||||
if (!removedKeys[propKey]) {
|
||||
continue;
|
||||
}
|
||||
var nextProp = obj[propKey];
|
||||
if (nextProp === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var attributeConfig = validAttributes[propKey];
|
||||
if (!attributeConfig) {
|
||||
continue; // not a valid native prop
|
||||
}
|
||||
|
||||
if (typeof nextProp === 'function') {
|
||||
nextProp = true;
|
||||
}
|
||||
if (typeof nextProp === 'undefined') {
|
||||
nextProp = null;
|
||||
}
|
||||
|
||||
if (typeof attributeConfig !== 'object') {
|
||||
// case: !Object is the default case
|
||||
updatePayload[propKey] = nextProp;
|
||||
} else if (typeof attributeConfig.diff === 'function' ||
|
||||
typeof attributeConfig.process === 'function') {
|
||||
// case: CustomAttributeConfiguration
|
||||
var nextValue = typeof attributeConfig.process === 'function' ?
|
||||
attributeConfig.process(nextProp) :
|
||||
nextProp;
|
||||
updatePayload[propKey] = nextValue;
|
||||
}
|
||||
removedKeys[propKey] = false;
|
||||
removedKeyCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function diffNestedArrayProperty(
|
||||
updatePayload:? Object,
|
||||
prevArray: Array<NestedNode>,
|
||||
nextArray: Array<NestedNode>,
|
||||
validAttributes: AttributeConfiguration
|
||||
) : ?Object {
|
||||
var minLength = prevArray.length < nextArray.length ?
|
||||
prevArray.length :
|
||||
nextArray.length;
|
||||
var i;
|
||||
for (i = 0; i < minLength; i++) {
|
||||
// Diff any items in the array in the forward direction. Repeated keys
|
||||
// will be overwritten by later values.
|
||||
updatePayload = diffNestedProperty(
|
||||
updatePayload,
|
||||
prevArray[i],
|
||||
nextArray[i],
|
||||
validAttributes
|
||||
);
|
||||
}
|
||||
for (; i < prevArray.length; i++) {
|
||||
// Clear out all remaining properties.
|
||||
updatePayload = clearNestedProperty(
|
||||
updatePayload,
|
||||
prevArray[i],
|
||||
validAttributes
|
||||
);
|
||||
}
|
||||
for (; i < nextArray.length; i++) {
|
||||
// Add all remaining properties.
|
||||
updatePayload = addNestedProperty(
|
||||
updatePayload,
|
||||
nextArray[i],
|
||||
validAttributes
|
||||
);
|
||||
}
|
||||
return updatePayload;
|
||||
}
|
||||
|
||||
function diffNestedProperty(
|
||||
updatePayload:? Object,
|
||||
prevProp: NestedNode,
|
||||
nextProp: NestedNode,
|
||||
validAttributes: AttributeConfiguration
|
||||
) : ?Object {
|
||||
|
||||
if (!updatePayload && prevProp === nextProp) {
|
||||
// If no properties have been added, then we can bail out quickly on object
|
||||
// equality.
|
||||
return updatePayload;
|
||||
}
|
||||
|
||||
if (!prevProp || !nextProp) {
|
||||
if (nextProp) {
|
||||
return addNestedProperty(
|
||||
updatePayload,
|
||||
nextProp,
|
||||
validAttributes
|
||||
);
|
||||
}
|
||||
if (prevProp) {
|
||||
return clearNestedProperty(
|
||||
updatePayload,
|
||||
prevProp,
|
||||
validAttributes
|
||||
);
|
||||
}
|
||||
return updatePayload;
|
||||
}
|
||||
|
||||
if (!Array.isArray(prevProp) && !Array.isArray(nextProp)) {
|
||||
// Both are leaves, we can diff the leaves.
|
||||
return diffProperties(
|
||||
updatePayload,
|
||||
resolveObject(prevProp),
|
||||
resolveObject(nextProp),
|
||||
validAttributes
|
||||
);
|
||||
}
|
||||
|
||||
if (Array.isArray(prevProp) && Array.isArray(nextProp)) {
|
||||
// Both are arrays, we can diff the arrays.
|
||||
return diffNestedArrayProperty(
|
||||
updatePayload,
|
||||
prevProp,
|
||||
nextProp,
|
||||
validAttributes
|
||||
);
|
||||
}
|
||||
|
||||
if (Array.isArray(prevProp)) {
|
||||
return diffProperties(
|
||||
updatePayload,
|
||||
// $FlowFixMe - We know that this is always an object when the input is.
|
||||
flattenStyle(prevProp),
|
||||
// $FlowFixMe - We know that this isn't an array because of above flow.
|
||||
resolveObject(nextProp),
|
||||
validAttributes
|
||||
);
|
||||
}
|
||||
|
||||
return diffProperties(
|
||||
updatePayload,
|
||||
resolveObject(prevProp),
|
||||
// $FlowFixMe - We know that this is always an object when the input is.
|
||||
flattenStyle(nextProp),
|
||||
validAttributes
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* addNestedProperty takes a single set of props and valid attribute
|
||||
* attribute configurations. It processes each prop and adds it to the
|
||||
* updatePayload.
|
||||
*/
|
||||
function addNestedProperty(
|
||||
updatePayload:? Object,
|
||||
nextProp: NestedNode,
|
||||
validAttributes: AttributeConfiguration
|
||||
) {
|
||||
if (!nextProp) {
|
||||
return updatePayload;
|
||||
}
|
||||
|
||||
if (!Array.isArray(nextProp)) {
|
||||
// Add each property of the leaf.
|
||||
return addProperties(
|
||||
updatePayload,
|
||||
resolveObject(nextProp),
|
||||
validAttributes
|
||||
);
|
||||
}
|
||||
|
||||
for (var i = 0; i < nextProp.length; i++) {
|
||||
// Add all the properties of the array.
|
||||
updatePayload = addNestedProperty(
|
||||
updatePayload,
|
||||
nextProp[i],
|
||||
validAttributes
|
||||
);
|
||||
}
|
||||
|
||||
return updatePayload;
|
||||
}
|
||||
|
||||
/**
|
||||
* clearNestedProperty takes a single set of props and valid attributes. It
|
||||
* adds a null sentinel to the updatePayload, for each prop key.
|
||||
*/
|
||||
function clearNestedProperty(
|
||||
updatePayload:? Object,
|
||||
prevProp: NestedNode,
|
||||
validAttributes: AttributeConfiguration
|
||||
) : ?Object {
|
||||
if (!prevProp) {
|
||||
return updatePayload;
|
||||
}
|
||||
|
||||
if (!Array.isArray(prevProp)) {
|
||||
// Add each property of the leaf.
|
||||
return clearProperties(
|
||||
updatePayload,
|
||||
resolveObject(prevProp),
|
||||
validAttributes
|
||||
);
|
||||
}
|
||||
|
||||
for (var i = 0; i < prevProp.length; i++) {
|
||||
// Add all the properties of the array.
|
||||
updatePayload = clearNestedProperty(
|
||||
updatePayload,
|
||||
prevProp[i],
|
||||
validAttributes
|
||||
);
|
||||
}
|
||||
return updatePayload;
|
||||
}
|
||||
|
||||
/**
|
||||
* diffProperties takes two sets of props and a set of valid attributes
|
||||
* and write to updatePayload the values that changed or were deleted.
|
||||
* If no updatePayload is provided, a new one is created and returned if
|
||||
* anything changed.
|
||||
*/
|
||||
function diffProperties(
|
||||
updatePayload: ?Object,
|
||||
prevProps: Object,
|
||||
nextProps: Object,
|
||||
validAttributes: AttributeConfiguration
|
||||
): ?Object {
|
||||
var attributeConfig : ?(CustomAttributeConfiguration | AttributeConfiguration);
|
||||
var nextProp;
|
||||
var prevProp;
|
||||
var altKey;
|
||||
|
||||
for (var propKey in nextProps) {
|
||||
attributeConfig = validAttributes[propKey];
|
||||
if (!attributeConfig) {
|
||||
continue; // not a valid native prop
|
||||
}
|
||||
|
||||
altKey = translateKey(propKey);
|
||||
if (!validAttributes[altKey]) {
|
||||
// If there is no config for the alternative, bail out. Helps ART.
|
||||
altKey = propKey;
|
||||
}
|
||||
|
||||
prevProp = prevProps[propKey];
|
||||
nextProp = nextProps[propKey];
|
||||
|
||||
// functions are converted to booleans as markers that the associated
|
||||
// events should be sent from native.
|
||||
if (typeof nextProp === 'function') {
|
||||
nextProp = (true : any);
|
||||
// If nextProp is not a function, then don't bother changing prevProp
|
||||
// since nextProp will win and go into the updatePayload regardless.
|
||||
if (typeof prevProp === 'function') {
|
||||
prevProp = (true : any);
|
||||
}
|
||||
}
|
||||
|
||||
// An explicit value of undefined is treated as a null because it overrides
|
||||
// any other preceeding value.
|
||||
if (typeof nextProp === 'undefined') {
|
||||
nextProp = (null : any);
|
||||
if (typeof prevProp === 'undefined') {
|
||||
prevProp = (null : any);
|
||||
}
|
||||
}
|
||||
|
||||
if (removedKeys) {
|
||||
removedKeys[propKey] = false;
|
||||
}
|
||||
|
||||
if (updatePayload && updatePayload[altKey] !== undefined) {
|
||||
// Something else already triggered an update to this key because another
|
||||
// value diffed. Since we're now later in the nested arrays our value is
|
||||
// more important so we need to calculate it and override the existing
|
||||
// value. It doesn't matter if nothing changed, we'll set it anyway.
|
||||
|
||||
// Pattern match on: attributeConfig
|
||||
if (typeof attributeConfig !== 'object') {
|
||||
// case: !Object is the default case
|
||||
updatePayload[altKey] = nextProp;
|
||||
} else if (typeof attributeConfig.diff === 'function' ||
|
||||
typeof attributeConfig.process === 'function') {
|
||||
// case: CustomAttributeConfiguration
|
||||
var nextValue = typeof attributeConfig.process === 'function' ?
|
||||
attributeConfig.process(nextProp) :
|
||||
nextProp;
|
||||
updatePayload[altKey] = nextValue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prevProp === nextProp) {
|
||||
continue; // nothing changed
|
||||
}
|
||||
|
||||
// Pattern match on: attributeConfig
|
||||
if (typeof attributeConfig !== 'object') {
|
||||
// case: !Object is the default case
|
||||
if (defaultDiffer(prevProp, nextProp)) {
|
||||
// a normal leaf has changed
|
||||
(updatePayload || (updatePayload = {}))[altKey] = nextProp;
|
||||
}
|
||||
} else if (typeof attributeConfig.diff === 'function' ||
|
||||
typeof attributeConfig.process === 'function') {
|
||||
// case: CustomAttributeConfiguration
|
||||
var shouldUpdate = prevProp === undefined || (
|
||||
typeof attributeConfig.diff === 'function' ?
|
||||
attributeConfig.diff(prevProp, nextProp) :
|
||||
defaultDiffer(prevProp, nextProp)
|
||||
);
|
||||
if (shouldUpdate) {
|
||||
nextValue = typeof attributeConfig.process === 'function' ?
|
||||
attributeConfig.process(nextProp) :
|
||||
nextProp;
|
||||
(updatePayload || (updatePayload = {}))[altKey] = nextValue;
|
||||
}
|
||||
} else {
|
||||
// default: fallthrough case when nested properties are defined
|
||||
removedKeys = null;
|
||||
removedKeyCount = 0;
|
||||
updatePayload = diffNestedProperty(
|
||||
updatePayload,
|
||||
prevProp,
|
||||
nextProp,
|
||||
attributeConfig
|
||||
);
|
||||
if (removedKeyCount > 0 && updatePayload) {
|
||||
restoreDeletedValuesInNestedArray(
|
||||
updatePayload,
|
||||
nextProp,
|
||||
attributeConfig
|
||||
);
|
||||
removedKeys = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also iterate through all the previous props to catch any that have been
|
||||
// removed and make sure native gets the signal so it can reset them to the
|
||||
// default.
|
||||
for (propKey in prevProps) {
|
||||
if (nextProps[propKey] !== undefined) {
|
||||
continue; // we've already covered this key in the previous pass
|
||||
}
|
||||
attributeConfig = validAttributes[propKey];
|
||||
if (!attributeConfig) {
|
||||
continue; // not a valid native prop
|
||||
}
|
||||
|
||||
altKey = translateKey(propKey);
|
||||
if (!attributeConfig[altKey]) {
|
||||
// If there is no config for the alternative, bail out. Helps ART.
|
||||
altKey = propKey;
|
||||
}
|
||||
|
||||
if (updatePayload && updatePayload[altKey] !== undefined) {
|
||||
// This was already updated to a diff result earlier.
|
||||
continue;
|
||||
}
|
||||
|
||||
prevProp = prevProps[propKey];
|
||||
if (prevProp === undefined) {
|
||||
continue; // was already empty anyway
|
||||
}
|
||||
// Pattern match on: attributeConfig
|
||||
if (typeof attributeConfig !== 'object' ||
|
||||
typeof attributeConfig.diff === 'function' ||
|
||||
typeof attributeConfig.process === 'function') {
|
||||
|
||||
// case: CustomAttributeConfiguration | !Object
|
||||
// Flag the leaf property for removal by sending a sentinel.
|
||||
(updatePayload || (updatePayload = {}))[altKey] = null;
|
||||
if (!removedKeys) {
|
||||
removedKeys = {};
|
||||
}
|
||||
if (!removedKeys[propKey]) {
|
||||
removedKeys[propKey] = true;
|
||||
removedKeyCount++;
|
||||
}
|
||||
} else {
|
||||
// default:
|
||||
// This is a nested attribute configuration where all the properties
|
||||
// were removed so we need to go through and clear out all of them.
|
||||
updatePayload = clearNestedProperty(
|
||||
updatePayload,
|
||||
prevProp,
|
||||
attributeConfig
|
||||
);
|
||||
}
|
||||
}
|
||||
return updatePayload;
|
||||
}
|
||||
|
||||
/**
|
||||
* addProperties adds all the valid props to the payload after being processed.
|
||||
*/
|
||||
function addProperties(
|
||||
updatePayload: ?Object,
|
||||
props: Object,
|
||||
validAttributes: AttributeConfiguration
|
||||
) : ?Object {
|
||||
// TODO: Fast path
|
||||
return diffProperties(updatePayload, emptyObject, props, validAttributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* clearProperties clears all the previous props by adding a null sentinel
|
||||
* to the payload for each valid key.
|
||||
*/
|
||||
function clearProperties(
|
||||
updatePayload: ?Object,
|
||||
prevProps: Object,
|
||||
validAttributes: AttributeConfiguration
|
||||
) :? Object {
|
||||
// TODO: Fast path
|
||||
return diffProperties(updatePayload, prevProps, emptyObject, validAttributes);
|
||||
}
|
||||
|
||||
var ReactNativeAttributePayload = {
|
||||
|
||||
create: function(
|
||||
props: Object,
|
||||
validAttributes: AttributeConfiguration
|
||||
) : ?Object {
|
||||
return addProperties(
|
||||
null, // updatePayload
|
||||
props,
|
||||
validAttributes
|
||||
);
|
||||
},
|
||||
|
||||
diff: function(
|
||||
prevProps: Object,
|
||||
nextProps: Object,
|
||||
validAttributes: AttributeConfiguration
|
||||
) : ?Object {
|
||||
return diffProperties(
|
||||
null, // updatePayload
|
||||
prevProps,
|
||||
nextProps,
|
||||
validAttributes
|
||||
);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = ReactNativeAttributePayload;
|
||||
230
src/renderers/native/ReactNative/ReactNativeBaseComponent.js
Normal file
230
src/renderers/native/ReactNative/ReactNativeBaseComponent.js
Normal file
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNativeBaseComponent
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var NativeMethodsMixin = require('NativeMethodsMixin');
|
||||
var ReactNativeAttributePayload = require('ReactNativeAttributePayload');
|
||||
var ReactNativeComponentTree = require('ReactNativeComponentTree');
|
||||
var ReactNativeEventEmitter = require('ReactNativeEventEmitter');
|
||||
var ReactNativeTagHandles = require('ReactNativeTagHandles');
|
||||
var ReactMultiChild = require('ReactMultiChild');
|
||||
var UIManager = require('UIManager');
|
||||
|
||||
var deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev');
|
||||
|
||||
var registrationNames = ReactNativeEventEmitter.registrationNames;
|
||||
var putListener = ReactNativeEventEmitter.putListener;
|
||||
var deleteListener = ReactNativeEventEmitter.deleteListener;
|
||||
var deleteAllListeners = ReactNativeEventEmitter.deleteAllListeners;
|
||||
|
||||
type ReactNativeBaseComponentViewConfig = {
|
||||
validAttributes: Object;
|
||||
uiViewClassName: string;
|
||||
}
|
||||
|
||||
// require('UIManagerStatTracker').install(); // uncomment to enable
|
||||
|
||||
/**
|
||||
* @constructor ReactNativeBaseComponent
|
||||
* @extends ReactComponent
|
||||
* @extends ReactMultiChild
|
||||
* @param {!object} UIKit View Configuration.
|
||||
*/
|
||||
var ReactNativeBaseComponent = function(
|
||||
viewConfig: ReactNativeBaseComponentViewConfig
|
||||
) {
|
||||
this.viewConfig = viewConfig;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mixin for containers that contain UIViews. NOTE: markup is rendered markup
|
||||
* which is a `viewID` ... see the return value for `mountComponent` !
|
||||
*/
|
||||
ReactNativeBaseComponent.Mixin = {
|
||||
getPublicInstance: function() {
|
||||
// TODO: This should probably use a composite wrapper
|
||||
return this;
|
||||
},
|
||||
|
||||
unmountComponent: function() {
|
||||
ReactNativeComponentTree.uncacheNode(this);
|
||||
deleteAllListeners(this);
|
||||
this.unmountChildren();
|
||||
this._rootNodeID = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Every native component is responsible for allocating its own `tag`, and
|
||||
* issuing the native `createView` command. But it is not responsible for
|
||||
* recording the fact that its own `rootNodeID` is associated with a
|
||||
* `nodeHandle`. Only the code that actually adds its `nodeHandle` (`tag`) as
|
||||
* a child of a container can confidently record that in
|
||||
* `ReactNativeTagHandles`.
|
||||
*/
|
||||
initializeChildren: function(children, containerTag, transaction, context) {
|
||||
var mountImages = this.mountChildren(children, transaction, context);
|
||||
// In a well balanced tree, half of the nodes are in the bottom row and have
|
||||
// no children - let's avoid calling out to the native bridge for a large
|
||||
// portion of the children.
|
||||
if (mountImages.length) {
|
||||
|
||||
// TODO: Pool these per platform view class. Reusing the `mountImages`
|
||||
// array would likely be a jit deopt.
|
||||
var createdTags = [];
|
||||
for (var i = 0, l = mountImages.length; i < l; i++) {
|
||||
var mountImage = mountImages[i];
|
||||
var childTag = mountImage;
|
||||
createdTags[i] = childTag;
|
||||
}
|
||||
UIManager.setChildren(containerTag, createdTags);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the component's currently mounted representation.
|
||||
*
|
||||
* @param {object} nextElement
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
* @param {object} context
|
||||
* @internal
|
||||
*/
|
||||
receiveComponent: function(nextElement, transaction, context) {
|
||||
var prevElement = this._currentElement;
|
||||
this._currentElement = nextElement;
|
||||
|
||||
if (__DEV__) {
|
||||
for (var key in this.viewConfig.validAttributes) {
|
||||
if (nextElement.props.hasOwnProperty(key)) {
|
||||
deepFreezeAndThrowOnMutationInDev(nextElement.props[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var updatePayload = ReactNativeAttributePayload.diff(
|
||||
prevElement.props,
|
||||
nextElement.props,
|
||||
this.viewConfig.validAttributes
|
||||
);
|
||||
|
||||
if (updatePayload) {
|
||||
UIManager.updateView(
|
||||
this._rootNodeID,
|
||||
this.viewConfig.uiViewClassName,
|
||||
updatePayload
|
||||
);
|
||||
}
|
||||
|
||||
this._reconcileListenersUponUpdate(
|
||||
prevElement.props,
|
||||
nextElement.props
|
||||
);
|
||||
this.updateChildren(nextElement.props.children, transaction, context);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {object} initialProps Native component props.
|
||||
*/
|
||||
_registerListenersUponCreation: function(initialProps) {
|
||||
for (var key in initialProps) {
|
||||
// NOTE: The check for `!props[key]`, is only possible because this method
|
||||
// registers listeners the *first* time a component is created.
|
||||
if (registrationNames[key] && initialProps[key]) {
|
||||
var listener = initialProps[key];
|
||||
putListener(this, key, listener);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Reconciles event listeners, adding or removing if necessary.
|
||||
* @param {object} prevProps Native component props including events.
|
||||
* @param {object} nextProps Next native component props including events.
|
||||
*/
|
||||
_reconcileListenersUponUpdate: function(prevProps, nextProps) {
|
||||
for (var key in nextProps) {
|
||||
if (registrationNames[key] && (nextProps[key] !== prevProps[key])) {
|
||||
if (nextProps[key]) {
|
||||
putListener(this, key, nextProps[key]);
|
||||
} else {
|
||||
deleteListener(this, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Currently this still uses IDs for reconciliation so this can return null.
|
||||
*
|
||||
* @return {null} Null.
|
||||
*/
|
||||
getNativeNode: function() {
|
||||
return this._rootNodeID;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} rootID Root ID of this subtree.
|
||||
* @param {Transaction} transaction For creating/updating.
|
||||
* @return {string} Unique iOS view tag.
|
||||
*/
|
||||
mountComponent: function(transaction, nativeParent, nativeContainerInfo, context) {
|
||||
var tag = ReactNativeTagHandles.allocateTag();
|
||||
|
||||
this._rootNodeID = tag;
|
||||
this._nativeParent = nativeParent;
|
||||
this._nativeContainerInfo = nativeContainerInfo;
|
||||
|
||||
if (__DEV__) {
|
||||
for (var key in this.viewConfig.validAttributes) {
|
||||
if (this._currentElement.props.hasOwnProperty(key)) {
|
||||
deepFreezeAndThrowOnMutationInDev(this._currentElement.props[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var updatePayload = ReactNativeAttributePayload.create(
|
||||
this._currentElement.props,
|
||||
this.viewConfig.validAttributes
|
||||
);
|
||||
|
||||
var nativeTopRootTag = nativeContainerInfo._tag;
|
||||
UIManager.createView(
|
||||
tag,
|
||||
this.viewConfig.uiViewClassName,
|
||||
nativeTopRootTag,
|
||||
updatePayload
|
||||
);
|
||||
|
||||
ReactNativeComponentTree.precacheNode(this, tag);
|
||||
|
||||
this._registerListenersUponCreation(this._currentElement.props);
|
||||
this.initializeChildren(
|
||||
this._currentElement.props.children,
|
||||
tag,
|
||||
transaction,
|
||||
context
|
||||
);
|
||||
return tag;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Order of mixins is important. ReactNativeBaseComponent overrides methods in
|
||||
* ReactMultiChild.
|
||||
*/
|
||||
Object.assign(
|
||||
ReactNativeBaseComponent.prototype,
|
||||
ReactMultiChild.Mixin,
|
||||
ReactNativeBaseComponent.Mixin,
|
||||
NativeMethodsMixin
|
||||
);
|
||||
|
||||
module.exports = ReactNativeBaseComponent;
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNativeComponentEnvironment
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var ReactNativeDOMIDOperations = require('ReactNativeDOMIDOperations');
|
||||
var ReactNativeReconcileTransaction = require('ReactNativeReconcileTransaction');
|
||||
|
||||
var ReactNativeComponentEnvironment = {
|
||||
|
||||
processChildrenUpdates: ReactNativeDOMIDOperations.dangerouslyProcessChildrenUpdates,
|
||||
|
||||
replaceNodeWithMarkup: ReactNativeDOMIDOperations.dangerouslyReplaceNodeWithMarkupByID,
|
||||
|
||||
/**
|
||||
* Nothing to do for UIKit bridge.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
unmountIDFromEnvironment: function(/*rootNodeID*/) {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {DOMElement} Element to clear.
|
||||
*/
|
||||
clearNode: function(/*containerView*/) {
|
||||
|
||||
},
|
||||
|
||||
ReactReconcileTransaction: ReactNativeReconcileTransaction,
|
||||
};
|
||||
|
||||
module.exports = ReactNativeComponentEnvironment;
|
||||
66
src/renderers/native/ReactNative/ReactNativeComponentTree.js
Normal file
66
src/renderers/native/ReactNative/ReactNativeComponentTree.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNativeComponentTree
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
var instanceCache = {};
|
||||
|
||||
/**
|
||||
* Drill down (through composites and empty components) until we get a native or
|
||||
* native text component.
|
||||
*
|
||||
* This is pretty polymorphic but unavoidable with the current structure we have
|
||||
* for `_renderedChildren`.
|
||||
*/
|
||||
function getRenderedNativeOrTextFromComponent(component) {
|
||||
var rendered;
|
||||
while ((rendered = component._renderedComponent)) {
|
||||
component = rendered;
|
||||
}
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate `_nativeNode` on the rendered native/text component with the given
|
||||
* DOM node. The passed `inst` can be a composite.
|
||||
*/
|
||||
function precacheNode(inst, tag) {
|
||||
var nativeInst = getRenderedNativeOrTextFromComponent(inst);
|
||||
instanceCache[tag] = nativeInst;
|
||||
}
|
||||
|
||||
function uncacheNode(inst) {
|
||||
var tag = inst._rootNodeID;
|
||||
if (tag) {
|
||||
delete instanceCache[tag];
|
||||
}
|
||||
}
|
||||
|
||||
function getInstanceFromTag(tag) {
|
||||
return instanceCache[tag] || null;
|
||||
}
|
||||
|
||||
function getTagFromInstance(inst) {
|
||||
invariant(inst._rootNodeID, 'All native instances should have a tag.');
|
||||
return inst._rootNodeID;
|
||||
}
|
||||
|
||||
var ReactNativeComponentTree = {
|
||||
getClosestInstanceFromNode: getInstanceFromTag,
|
||||
getInstanceFromNode: getInstanceFromTag,
|
||||
getNodeFromInstance: getTagFromInstance,
|
||||
precacheNode: precacheNode,
|
||||
uncacheNode: uncacheNode,
|
||||
};
|
||||
|
||||
module.exports = ReactNativeComponentTree;
|
||||
21
src/renderers/native/ReactNative/ReactNativeContainerInfo.js
Normal file
21
src/renderers/native/ReactNative/ReactNativeContainerInfo.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNativeContainerInfo
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
function ReactNativeContainerInfo(tag) {
|
||||
var info = {
|
||||
_tag: tag,
|
||||
};
|
||||
return info;
|
||||
}
|
||||
|
||||
module.exports = ReactNativeContainerInfo;
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNativeDOMIDOperations
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var ReactNativeComponentTree = require('ReactNativeComponentTree');
|
||||
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var UIManager = require('UIManager');
|
||||
|
||||
/**
|
||||
* Updates a component's children by processing a series of updates.
|
||||
* For each of the update/create commands, the `fromIndex` refers to the index
|
||||
* that the item existed at *before* any of the updates are applied, and the
|
||||
* `toIndex` refers to the index after *all* of the updates are applied
|
||||
* (including deletes/moves). TODO: refactor so this can be shared with
|
||||
* DOMChildrenOperations.
|
||||
*
|
||||
* @param {ReactNativeBaseComponent} updates List of update configurations.
|
||||
* @param {array<string>} markup List of markup strings - in the case of React
|
||||
* IOS, the ids of new components assumed to be already created.
|
||||
*/
|
||||
var dangerouslyProcessChildrenUpdates = function(inst, childrenUpdates) {
|
||||
if (!childrenUpdates.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var containerTag = ReactNativeComponentTree.getNodeFromInstance(inst);
|
||||
|
||||
var moveFromIndices;
|
||||
var moveToIndices;
|
||||
var addChildTags;
|
||||
var addAtIndices;
|
||||
var removeAtIndices;
|
||||
|
||||
for (var i = 0; i < childrenUpdates.length; i++) {
|
||||
var update = childrenUpdates[i];
|
||||
if (update.type === ReactMultiChildUpdateTypes.MOVE_EXISTING) {
|
||||
(moveFromIndices || (moveFromIndices = [])).push(update.fromIndex);
|
||||
(moveToIndices || (moveToIndices = [])).push(update.toIndex);
|
||||
} else if (update.type === ReactMultiChildUpdateTypes.REMOVE_NODE) {
|
||||
(removeAtIndices || (removeAtIndices = [])).push(update.fromIndex);
|
||||
} else if (update.type === ReactMultiChildUpdateTypes.INSERT_MARKUP) {
|
||||
var mountImage = update.content;
|
||||
var tag = mountImage;
|
||||
(addAtIndices || (addAtIndices = [])).push(update.toIndex);
|
||||
(addChildTags || (addChildTags = [])).push(tag);
|
||||
}
|
||||
}
|
||||
|
||||
UIManager.manageChildren(
|
||||
containerTag,
|
||||
moveFromIndices,
|
||||
moveToIndices,
|
||||
addChildTags,
|
||||
addAtIndices,
|
||||
removeAtIndices
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Operations used to process updates to DOM nodes. This is made injectable via
|
||||
* `ReactComponent.DOMIDOperations`.
|
||||
*/
|
||||
var ReactNativeDOMIDOperations = {
|
||||
dangerouslyProcessChildrenUpdates: ReactPerf.measure(
|
||||
// FIXME(frantic): #4441289 Hack to avoid modifying react-tools
|
||||
'ReactDOMIDOperations',
|
||||
'dangerouslyProcessChildrenUpdates',
|
||||
dangerouslyProcessChildrenUpdates
|
||||
),
|
||||
|
||||
/**
|
||||
* Replaces a view that exists in the document with markup.
|
||||
*
|
||||
* @param {string} id ID of child to be replaced.
|
||||
* @param {string} markup Mount image to replace child with id.
|
||||
*/
|
||||
dangerouslyReplaceNodeWithMarkupByID: ReactPerf.measure(
|
||||
'ReactDOMIDOperations',
|
||||
'dangerouslyReplaceNodeWithMarkupByID',
|
||||
function(id, mountImage) {
|
||||
var oldTag = id;
|
||||
UIManager.replaceExistingNonRootView(oldTag, mountImage);
|
||||
}
|
||||
),
|
||||
};
|
||||
|
||||
module.exports = ReactNativeDOMIDOperations;
|
||||
108
src/renderers/native/ReactNative/ReactNativeDefaultInjection.js
Normal file
108
src/renderers/native/ReactNative/ReactNativeDefaultInjection.js
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNativeDefaultInjection
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Make sure essential globals are available and are patched correctly. Please don't remove this
|
||||
* line. Bundles created by react-packager `require` it before executing any application code. This
|
||||
* ensures it exists in the dependency graph and can be `require`d.
|
||||
*/
|
||||
require('InitializeJavaScriptAppEngine');
|
||||
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var EventPluginUtils = require('EventPluginUtils');
|
||||
var IOSDefaultEventPluginOrder = require('IOSDefaultEventPluginOrder');
|
||||
var IOSNativeBridgeEventPlugin = require('IOSNativeBridgeEventPlugin');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactComponentEnvironment = require('ReactComponentEnvironment');
|
||||
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
|
||||
var ReactEmptyComponent = require('ReactEmptyComponent');
|
||||
var ReactNativeComponentEnvironment = require('ReactNativeComponentEnvironment');
|
||||
var ReactNativeGlobalResponderHandler = require('ReactNativeGlobalResponderHandler');
|
||||
var ReactNativeTextComponent = require('ReactNativeTextComponent');
|
||||
var ReactNativeTreeTraversal = require('ReactNativeTreeTraversal');
|
||||
var ReactNativeComponent = require('ReactNativeComponent');
|
||||
var ReactNativeComponentTree = require('ReactNativeComponentTree');
|
||||
var ReactSimpleEmptyComponent = require('ReactSimpleEmptyComponent');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
var ResponderEventPlugin = require('ResponderEventPlugin');
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
// Just to ensure this gets packaged, since its only caller is from Native.
|
||||
require('RCTEventEmitter');
|
||||
require('RCTLog');
|
||||
require('JSTimersExecution');
|
||||
|
||||
function inject() {
|
||||
/**
|
||||
* Inject module for resolving DOM hierarchy and plugin ordering.
|
||||
*/
|
||||
EventPluginHub.injection.injectEventPluginOrder(IOSDefaultEventPluginOrder);
|
||||
EventPluginUtils.injection.injectComponentTree(ReactNativeComponentTree);
|
||||
EventPluginUtils.injection.injectTreeTraversal(ReactNativeTreeTraversal);
|
||||
|
||||
ResponderEventPlugin.injection.injectGlobalResponderHandler(
|
||||
ReactNativeGlobalResponderHandler
|
||||
);
|
||||
|
||||
/**
|
||||
* Some important event plugins included by default (without having to require
|
||||
* them).
|
||||
*/
|
||||
EventPluginHub.injection.injectEventPluginsByName({
|
||||
'ResponderEventPlugin': ResponderEventPlugin,
|
||||
'IOSNativeBridgeEventPlugin': IOSNativeBridgeEventPlugin,
|
||||
});
|
||||
|
||||
ReactUpdates.injection.injectReconcileTransaction(
|
||||
ReactNativeComponentEnvironment.ReactReconcileTransaction
|
||||
);
|
||||
|
||||
ReactUpdates.injection.injectBatchingStrategy(
|
||||
ReactDefaultBatchingStrategy
|
||||
);
|
||||
|
||||
ReactComponentEnvironment.injection.injectEnvironment(
|
||||
ReactNativeComponentEnvironment
|
||||
);
|
||||
|
||||
var EmptyComponent = (instantiate) => {
|
||||
// Can't import View at the top because it depends on React to make its composite
|
||||
var View = require('View');
|
||||
return new ReactSimpleEmptyComponent(
|
||||
ReactElement.createElement(View, {
|
||||
collapsable: true,
|
||||
style: { position: 'absolute' },
|
||||
}),
|
||||
instantiate
|
||||
);
|
||||
};
|
||||
|
||||
ReactEmptyComponent.injection.injectEmptyComponentFactory(EmptyComponent);
|
||||
|
||||
ReactNativeComponent.injection.injectTextComponentClass(
|
||||
ReactNativeTextComponent
|
||||
);
|
||||
ReactNativeComponent.injection.injectGenericComponentClass(function(tag) {
|
||||
// Show a nicer error message for non-function tags
|
||||
var info = '';
|
||||
if (typeof tag === 'string' && /^[a-z]/.test(tag)) {
|
||||
info += ' Each component name should start with an uppercase letter.';
|
||||
}
|
||||
invariant(false, 'Expected a component class, got %s.%s', tag, info);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
inject: inject,
|
||||
};
|
||||
223
src/renderers/native/ReactNative/ReactNativeEventEmitter.js
Normal file
223
src/renderers/native/ReactNative/ReactNativeEventEmitter.js
Normal file
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNativeEventEmitter
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var EventPluginRegistry = require('EventPluginRegistry');
|
||||
var ReactEventEmitterMixin = require('ReactEventEmitterMixin');
|
||||
var ReactNativeComponentTree = require('ReactNativeComponentTree');
|
||||
var ReactNativeTagHandles = require('ReactNativeTagHandles');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
var EventConstants = require('EventConstants');
|
||||
|
||||
var merge = require('merge');
|
||||
var warning = require('warning');
|
||||
|
||||
var topLevelTypes = EventConstants.topLevelTypes;
|
||||
|
||||
/**
|
||||
* Version of `ReactBrowserEventEmitter` that works on the receiving side of a
|
||||
* serialized worker boundary.
|
||||
*/
|
||||
|
||||
// Shared default empty native event - conserve memory.
|
||||
var EMPTY_NATIVE_EVENT = {};
|
||||
|
||||
/**
|
||||
* Selects a subsequence of `Touch`es, without destroying `touches`.
|
||||
*
|
||||
* @param {Array<Touch>} touches Deserialized touch objects.
|
||||
* @param {Array<number>} indices Indices by which to pull subsequence.
|
||||
* @return {Array<Touch>} Subsequence of touch objects.
|
||||
*/
|
||||
var touchSubsequence = function(touches, indices) {
|
||||
var ret = [];
|
||||
for (var i = 0; i < indices.length; i++) {
|
||||
ret.push(touches[indices[i]]);
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO: Pool all of this.
|
||||
*
|
||||
* Destroys `touches` by removing touch objects at indices `indices`. This is
|
||||
* to maintain compatibility with W3C touch "end" events, where the active
|
||||
* touches don't include the set that has just been "ended".
|
||||
*
|
||||
* @param {Array<Touch>} touches Deserialized touch objects.
|
||||
* @param {Array<number>} indices Indices to remove from `touches`.
|
||||
* @return {Array<Touch>} Subsequence of removed touch objects.
|
||||
*/
|
||||
var removeTouchesAtIndices = function(
|
||||
touches: Array<Object>,
|
||||
indices: Array<number>
|
||||
): Array<Object> {
|
||||
var rippedOut = [];
|
||||
// use an unsafe downcast to alias to nullable elements,
|
||||
// so we can delete and then compact.
|
||||
var temp: Array<?Object> = (touches: Array<any>);
|
||||
for (var i = 0; i < indices.length; i++) {
|
||||
var index = indices[i];
|
||||
rippedOut.push(touches[index]);
|
||||
temp[index] = null;
|
||||
}
|
||||
var fillAt = 0;
|
||||
for (var j = 0; j < temp.length; j++) {
|
||||
var cur = temp[j];
|
||||
if (cur !== null) {
|
||||
temp[fillAt++] = cur;
|
||||
}
|
||||
}
|
||||
temp.length = fillAt;
|
||||
return rippedOut;
|
||||
};
|
||||
|
||||
/**
|
||||
* `ReactNativeEventEmitter` is used to attach top-level event listeners. For example:
|
||||
*
|
||||
* ReactNativeEventEmitter.putListener('myID', 'onClick', myFunction);
|
||||
*
|
||||
* This would allocate a "registration" of `('onClick', myFunction)` on 'myID'.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
var ReactNativeEventEmitter = merge(ReactEventEmitterMixin, {
|
||||
|
||||
registrationNames: EventPluginRegistry.registrationNameModules,
|
||||
|
||||
putListener: EventPluginHub.putListener,
|
||||
|
||||
getListener: EventPluginHub.getListener,
|
||||
|
||||
deleteListener: EventPluginHub.deleteListener,
|
||||
|
||||
deleteAllListeners: EventPluginHub.deleteAllListeners,
|
||||
|
||||
/**
|
||||
* Internal version of `receiveEvent` in terms of normalized (non-tag)
|
||||
* `rootNodeID`.
|
||||
*
|
||||
* @see receiveEvent.
|
||||
*
|
||||
* @param {rootNodeID} rootNodeID React root node ID that event occurred on.
|
||||
* @param {TopLevelType} topLevelType Top level type of event.
|
||||
* @param {object} nativeEventParam Object passed from native.
|
||||
*/
|
||||
_receiveRootNodeIDEvent: function(
|
||||
rootNodeID: number,
|
||||
topLevelType: string,
|
||||
nativeEventParam: Object
|
||||
) {
|
||||
var nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT;
|
||||
var inst = ReactNativeComponentTree.getInstanceFromNode(rootNodeID);
|
||||
if (!inst) {
|
||||
// If the original instance is already gone, we don't have to dispatch
|
||||
// any events.
|
||||
return;
|
||||
}
|
||||
ReactUpdates.batchedUpdates(function() {
|
||||
ReactNativeEventEmitter.handleTopLevel(
|
||||
topLevelType,
|
||||
inst,
|
||||
nativeEvent,
|
||||
nativeEvent.target
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Publicly exposed method on module for native objc to invoke when a top
|
||||
* level event is extracted.
|
||||
* @param {rootNodeID} rootNodeID React root node ID that event occurred on.
|
||||
* @param {TopLevelType} topLevelType Top level type of event.
|
||||
* @param {object} nativeEventParam Object passed from native.
|
||||
*/
|
||||
receiveEvent: function(
|
||||
tag: number,
|
||||
topLevelType: string,
|
||||
nativeEventParam: Object
|
||||
) {
|
||||
var rootNodeID = tag;
|
||||
ReactNativeEventEmitter._receiveRootNodeIDEvent(
|
||||
rootNodeID,
|
||||
topLevelType,
|
||||
nativeEventParam
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Simple multi-wrapper around `receiveEvent` that is intended to receive an
|
||||
* efficient representation of `Touch` objects, and other information that
|
||||
* can be used to construct W3C compliant `Event` and `Touch` lists.
|
||||
*
|
||||
* This may create dispatch behavior that differs than web touch handling. We
|
||||
* loop through each of the changed touches and receive it as a single event.
|
||||
* So two `touchStart`/`touchMove`s that occur simultaneously are received as
|
||||
* two separate touch event dispatches - when they arguably should be one.
|
||||
*
|
||||
* This implementation reuses the `Touch` objects themselves as the `Event`s
|
||||
* since we dispatch an event for each touch (though that might not be spec
|
||||
* compliant). The main purpose of reusing them is to save allocations.
|
||||
*
|
||||
* TODO: Dispatch multiple changed touches in one event. The bubble path
|
||||
* could be the first common ancestor of all the `changedTouches`.
|
||||
*
|
||||
* One difference between this behavior and W3C spec: cancelled touches will
|
||||
* not appear in `.touches`, or in any future `.touches`, though they may
|
||||
* still be "actively touching the surface".
|
||||
*
|
||||
* Web desktop polyfills only need to construct a fake touch event with
|
||||
* identifier 0, also abandoning traditional click handlers.
|
||||
*/
|
||||
receiveTouches: function(
|
||||
eventTopLevelType: string,
|
||||
touches: Array<Object>,
|
||||
changedIndices: Array<number>
|
||||
) {
|
||||
var changedTouches =
|
||||
eventTopLevelType === topLevelTypes.topTouchEnd ||
|
||||
eventTopLevelType === topLevelTypes.topTouchCancel ?
|
||||
removeTouchesAtIndices(touches, changedIndices) :
|
||||
touchSubsequence(touches, changedIndices);
|
||||
|
||||
for (var jj = 0; jj < changedTouches.length; jj++) {
|
||||
var touch = changedTouches[jj];
|
||||
// Touch objects can fulfill the role of `DOM` `Event` objects if we set
|
||||
// the `changedTouches`/`touches`. This saves allocations.
|
||||
touch.changedTouches = changedTouches;
|
||||
touch.touches = touches;
|
||||
var nativeEvent = touch;
|
||||
var rootNodeID = null;
|
||||
var target = nativeEvent.target;
|
||||
if (target !== null && target !== undefined) {
|
||||
if (target < ReactNativeTagHandles.tagsStartAt) {
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
false,
|
||||
'A view is reporting that a touch occured on tag zero.'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
rootNodeID = target;
|
||||
}
|
||||
}
|
||||
ReactNativeEventEmitter._receiveRootNodeIDEvent(
|
||||
rootNodeID,
|
||||
eventTopLevelType,
|
||||
nativeEvent
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = ReactNativeEventEmitter;
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNativeGlobalResponderHandler
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var UIManager = require('UIManager');
|
||||
|
||||
var ReactNativeGlobalResponderHandler = {
|
||||
onChange: function(from, to, blockNativeResponder) {
|
||||
if (to !== null) {
|
||||
UIManager.setJSResponder(
|
||||
to._rootNodeID,
|
||||
blockNativeResponder
|
||||
);
|
||||
} else {
|
||||
UIManager.clearJSResponder();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = ReactNativeGlobalResponderHandler;
|
||||
238
src/renderers/native/ReactNative/ReactNativeMount.js
Normal file
238
src/renderers/native/ReactNative/ReactNativeMount.js
Normal file
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNativeMount
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactNativeContainerInfo = require('ReactNativeContainerInfo');
|
||||
var ReactNativeTagHandles = require('ReactNativeTagHandles');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var ReactReconciler = require('ReactReconciler');
|
||||
var ReactUpdateQueue = require('ReactUpdateQueue');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
var UIManager = require('UIManager');
|
||||
|
||||
var emptyObject = require('emptyObject');
|
||||
var instantiateReactComponent = require('instantiateReactComponent');
|
||||
var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
|
||||
|
||||
/**
|
||||
* Temporary (?) hack so that we can store all top-level pending updates on
|
||||
* composites instead of having to worry about different types of components
|
||||
* here.
|
||||
*/
|
||||
var TopLevelWrapper = function() {};
|
||||
TopLevelWrapper.prototype.isReactComponent = {};
|
||||
if (__DEV__) {
|
||||
TopLevelWrapper.displayName = 'TopLevelWrapper';
|
||||
}
|
||||
TopLevelWrapper.prototype.render = function() {
|
||||
// this.props is actually a ReactElement
|
||||
return this.props;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mounts this component and inserts it into the DOM.
|
||||
*
|
||||
* @param {ReactComponent} componentInstance The instance to mount.
|
||||
* @param {number} rootID ID of the root node.
|
||||
* @param {number} containerTag container element to mount into.
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
*/
|
||||
function mountComponentIntoNode(
|
||||
componentInstance,
|
||||
containerTag,
|
||||
transaction) {
|
||||
var markup = ReactReconciler.mountComponent(
|
||||
componentInstance,
|
||||
transaction,
|
||||
null,
|
||||
ReactNativeContainerInfo(containerTag),
|
||||
emptyObject
|
||||
);
|
||||
componentInstance._renderedComponent._topLevelWrapper = componentInstance;
|
||||
ReactNativeMount._mountImageIntoNode(markup, containerTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Batched mount.
|
||||
*
|
||||
* @param {ReactComponent} componentInstance The instance to mount.
|
||||
* @param {number} rootID ID of the root node.
|
||||
* @param {number} containerTag container element to mount into.
|
||||
*/
|
||||
function batchedMountComponentIntoNode(
|
||||
componentInstance,
|
||||
containerTag) {
|
||||
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
|
||||
transaction.perform(
|
||||
mountComponentIntoNode,
|
||||
null,
|
||||
componentInstance,
|
||||
containerTag,
|
||||
transaction
|
||||
);
|
||||
ReactUpdates.ReactReconcileTransaction.release(transaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* As soon as `ReactMount` is refactored to not rely on the DOM, we can share
|
||||
* code between the two. For now, we'll hard code the ID logic.
|
||||
*/
|
||||
var ReactNativeMount = {
|
||||
_instancesByContainerID: {},
|
||||
|
||||
// these two functions are needed by React Devtools
|
||||
findNodeHandle: require('findNodeHandle'),
|
||||
|
||||
/**
|
||||
* @param {ReactComponent} instance Instance to render.
|
||||
* @param {containerTag} containerView Handle to native view tag
|
||||
*/
|
||||
renderComponent: function(
|
||||
nextElement: ReactElement,
|
||||
containerTag: number,
|
||||
callback?: ?(() => void)
|
||||
): ?ReactComponent {
|
||||
var nextWrappedElement = new ReactElement(
|
||||
TopLevelWrapper,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
nextElement
|
||||
);
|
||||
|
||||
var topRootNodeID = containerTag;
|
||||
var prevComponent = ReactNativeMount._instancesByContainerID[topRootNodeID];
|
||||
if (prevComponent) {
|
||||
var prevWrappedElement = prevComponent._currentElement;
|
||||
var prevElement = prevWrappedElement.props;
|
||||
if (shouldUpdateReactComponent(prevElement, nextElement)) {
|
||||
ReactUpdateQueue.enqueueElementInternal(prevComponent, nextWrappedElement);
|
||||
if (callback) {
|
||||
ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback);
|
||||
}
|
||||
return prevComponent;
|
||||
} else {
|
||||
ReactNativeMount.unmountComponentAtNode(containerTag);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ReactNativeTagHandles.reactTagIsNativeTopRootID(containerTag)) {
|
||||
console.error('You cannot render into anything but a top root');
|
||||
return null;
|
||||
}
|
||||
|
||||
ReactNativeTagHandles.assertRootTag(containerTag);
|
||||
|
||||
var instance = instantiateReactComponent(nextWrappedElement);
|
||||
ReactNativeMount._instancesByContainerID[containerTag] = instance;
|
||||
|
||||
// The initial render is synchronous but any updates that happen during
|
||||
// rendering, in componentWillMount or componentDidMount, will be batched
|
||||
// according to the current batching strategy.
|
||||
|
||||
ReactUpdates.batchedUpdates(
|
||||
batchedMountComponentIntoNode,
|
||||
instance,
|
||||
containerTag
|
||||
);
|
||||
var component = instance.getPublicInstance();
|
||||
if (callback) {
|
||||
callback.call(component);
|
||||
}
|
||||
return component;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {View} view View tree image.
|
||||
* @param {number} containerViewID View to insert sub-view into.
|
||||
*/
|
||||
_mountImageIntoNode: ReactPerf.measure(
|
||||
// FIXME(frantic): #4441289 Hack to avoid modifying react-tools
|
||||
'ReactComponentBrowserEnvironment',
|
||||
'mountImageIntoNode',
|
||||
function(mountImage, containerID) {
|
||||
// Since we now know that the `mountImage` has been mounted, we can
|
||||
// mark it as such.
|
||||
var childTag = mountImage;
|
||||
UIManager.setChildren(
|
||||
containerID,
|
||||
[childTag]
|
||||
);
|
||||
}
|
||||
),
|
||||
|
||||
/**
|
||||
* Standard unmounting of the component that is rendered into `containerID`,
|
||||
* but will also execute a command to remove the actual container view
|
||||
* itself. This is useful when a client is cleaning up a React tree, and also
|
||||
* knows that the container will no longer be needed. When executing
|
||||
* asynchronously, it's easier to just have this method be the one that calls
|
||||
* for removal of the view.
|
||||
*/
|
||||
unmountComponentAtNodeAndRemoveContainer: function(
|
||||
containerTag: number
|
||||
) {
|
||||
ReactNativeMount.unmountComponentAtNode(containerTag);
|
||||
// call back into native to remove all of the subviews from this container
|
||||
UIManager.removeRootView(containerTag);
|
||||
},
|
||||
|
||||
/**
|
||||
* Unmount component at container ID by iterating through each child component
|
||||
* that has been rendered and unmounting it. There should just be one child
|
||||
* component at this time.
|
||||
*/
|
||||
unmountComponentAtNode: function(containerTag: number): boolean {
|
||||
if (!ReactNativeTagHandles.reactTagIsNativeTopRootID(containerTag)) {
|
||||
console.error('You cannot render into anything but a top root');
|
||||
return false;
|
||||
}
|
||||
|
||||
var instance = ReactNativeMount._instancesByContainerID[containerTag];
|
||||
if (!instance) {
|
||||
return false;
|
||||
}
|
||||
ReactNativeMount.unmountComponentFromNode(instance, containerTag);
|
||||
delete ReactNativeMount._instancesByContainerID[containerTag];
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Unmounts a component and sends messages back to iOS to remove its subviews.
|
||||
*
|
||||
* @param {ReactComponent} instance React component instance.
|
||||
* @param {string} containerID ID of container we're removing from.
|
||||
* @final
|
||||
* @internal
|
||||
* @see {ReactNativeMount.unmountComponentAtNode}
|
||||
*/
|
||||
unmountComponentFromNode: function(
|
||||
instance: ReactComponent,
|
||||
containerID: string
|
||||
) {
|
||||
// Call back into native to remove all of the subviews from this container
|
||||
ReactReconciler.unmountComponent(instance);
|
||||
UIManager.removeSubviewsFromContainerWithID(containerID);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
ReactNativeMount.renderComponent = ReactPerf.measure(
|
||||
'ReactMount',
|
||||
'_renderNewRootComponent',
|
||||
ReactNativeMount.renderComponent
|
||||
);
|
||||
|
||||
module.exports = ReactNativeMount;
|
||||
44
src/renderers/native/ReactNative/ReactNativePropRegistry.js
Normal file
44
src/renderers/native/ReactNative/ReactNativePropRegistry.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNativePropRegistry
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var objects = {};
|
||||
var uniqueID = 1;
|
||||
var emptyObject = {};
|
||||
|
||||
class ReactNativePropRegistry {
|
||||
static register(object: Object): number {
|
||||
var id = ++uniqueID;
|
||||
if (__DEV__) {
|
||||
Object.freeze(object);
|
||||
}
|
||||
objects[id] = object;
|
||||
return id;
|
||||
}
|
||||
|
||||
static getByID(id: number): Object {
|
||||
if (!id) {
|
||||
// Used in the style={[condition && id]} pattern,
|
||||
// we want it to be a no-op when the value is false or null
|
||||
return emptyObject;
|
||||
}
|
||||
|
||||
var object = objects[id];
|
||||
if (!object) {
|
||||
console.warn('Invalid style with id `' + id + '`. Skipping ...');
|
||||
return emptyObject;
|
||||
}
|
||||
return object;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ReactNativePropRegistry;
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNativeReconcileTransaction
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var CallbackQueue = require('CallbackQueue');
|
||||
var PooledClass = require('PooledClass');
|
||||
var Transaction = require('Transaction');
|
||||
|
||||
/**
|
||||
* Provides a `CallbackQueue` queue for collecting `onDOMReady` callbacks during
|
||||
* the performing of the transaction.
|
||||
*/
|
||||
var ON_DOM_READY_QUEUEING = {
|
||||
/**
|
||||
* Initializes the internal `onDOMReady` queue.
|
||||
*/
|
||||
initialize: function() {
|
||||
this.reactMountReady.reset();
|
||||
},
|
||||
|
||||
/**
|
||||
* After DOM is flushed, invoke all registered `onDOMReady` callbacks.
|
||||
*/
|
||||
close: function() {
|
||||
this.reactMountReady.notifyAll();
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Executed within the scope of the `Transaction` instance. Consider these as
|
||||
* being member methods, but with an implied ordering while being isolated from
|
||||
* each other.
|
||||
*/
|
||||
var TRANSACTION_WRAPPERS = [ON_DOM_READY_QUEUEING];
|
||||
|
||||
/**
|
||||
* Currently:
|
||||
* - The order that these are listed in the transaction is critical:
|
||||
* - Suppresses events.
|
||||
* - Restores selection range.
|
||||
*
|
||||
* Future:
|
||||
* - Restore document/overflow scroll positions that were unintentionally
|
||||
* modified via DOM insertions above the top viewport boundary.
|
||||
* - Implement/integrate with customized constraint based layout system and keep
|
||||
* track of which dimensions must be remeasured.
|
||||
*
|
||||
* @class ReactNativeReconcileTransaction
|
||||
*/
|
||||
function ReactNativeReconcileTransaction() {
|
||||
this.reinitializeTransaction();
|
||||
this.reactMountReady = CallbackQueue.getPooled(null);
|
||||
}
|
||||
|
||||
var Mixin = {
|
||||
/**
|
||||
* @see Transaction
|
||||
* @abstract
|
||||
* @final
|
||||
* @return {array<object>} List of operation wrap procedures.
|
||||
* TODO: convert to array<TransactionWrapper>
|
||||
*/
|
||||
getTransactionWrappers: function() {
|
||||
return TRANSACTION_WRAPPERS;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {object} The queue to collect `onDOMReady` callbacks with.
|
||||
* TODO: convert to ReactMountReady
|
||||
*/
|
||||
getReactMountReady: function() {
|
||||
return this.reactMountReady;
|
||||
},
|
||||
|
||||
/**
|
||||
* `PooledClass` looks for this, and will invoke this before allowing this
|
||||
* instance to be reused.
|
||||
*/
|
||||
destructor: function() {
|
||||
CallbackQueue.release(this.reactMountReady);
|
||||
this.reactMountReady = null;
|
||||
},
|
||||
};
|
||||
|
||||
Object.assign(
|
||||
ReactNativeReconcileTransaction.prototype,
|
||||
Transaction.Mixin,
|
||||
ReactNativeReconcileTransaction,
|
||||
Mixin
|
||||
);
|
||||
|
||||
PooledClass.addPoolingTo(ReactNativeReconcileTransaction);
|
||||
|
||||
module.exports = ReactNativeReconcileTransaction;
|
||||
57
src/renderers/native/ReactNative/ReactNativeTagHandles.js
Normal file
57
src/renderers/native/ReactNative/ReactNativeTagHandles.js
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNativeTagHandles
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
/**
|
||||
* Keeps track of allocating and associating native "tags" which are numeric,
|
||||
* unique view IDs. All the native tags are negative numbers, to avoid
|
||||
* collisions, but in the JS we keep track of them as positive integers to store
|
||||
* them effectively in Arrays. So we must refer to them as "inverses" of the
|
||||
* native tags (that are * normally negative).
|
||||
*
|
||||
* It *must* be the case that every `rootNodeID` always maps to the exact same
|
||||
* `tag` forever. The easiest way to accomplish this is to never delete
|
||||
* anything from this table.
|
||||
* Why: Because `dangerouslyReplaceNodeWithMarkupByID` relies on being able to
|
||||
* unmount a component with a `rootNodeID`, then mount a new one in its place,
|
||||
*/
|
||||
var INITIAL_TAG_COUNT = 1;
|
||||
var ReactNativeTagHandles = {
|
||||
tagsStartAt: INITIAL_TAG_COUNT,
|
||||
tagCount: INITIAL_TAG_COUNT,
|
||||
|
||||
allocateTag: function(): number {
|
||||
// Skip over root IDs as those are reserved for native
|
||||
while (this.reactTagIsNativeTopRootID(ReactNativeTagHandles.tagCount)) {
|
||||
ReactNativeTagHandles.tagCount++;
|
||||
}
|
||||
var tag = ReactNativeTagHandles.tagCount;
|
||||
ReactNativeTagHandles.tagCount++;
|
||||
return tag;
|
||||
},
|
||||
|
||||
assertRootTag: function(tag: number): void {
|
||||
invariant(
|
||||
this.reactTagIsNativeTopRootID(tag),
|
||||
'Expect a native root tag, instead got %s', tag
|
||||
);
|
||||
},
|
||||
|
||||
reactTagIsNativeTopRootID: function(reactTag: number): bool {
|
||||
// We reserve all tags that are 1 mod 10 for native root views
|
||||
return reactTag % 10 === 1;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = ReactNativeTagHandles;
|
||||
81
src/renderers/native/ReactNative/ReactNativeTextComponent.js
Normal file
81
src/renderers/native/ReactNative/ReactNativeTextComponent.js
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNativeTextComponent
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var ReactNativeComponentTree = require('ReactNativeComponentTree');
|
||||
var ReactNativeTagHandles = require('ReactNativeTagHandles');
|
||||
var UIManager = require('UIManager');
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
var ReactNativeTextComponent = function(text) {
|
||||
// This is really a ReactText (ReactNode), not a ReactElement
|
||||
this._currentElement = text;
|
||||
this._stringText = '' + text;
|
||||
this._nativeParent = null;
|
||||
this._rootNodeID = null;
|
||||
};
|
||||
|
||||
Object.assign(ReactNativeTextComponent.prototype, {
|
||||
|
||||
mountComponent: function(transaction, nativeParent, nativeContainerInfo, context) {
|
||||
// TODO: nativeParent should have this context already. Stop abusing context.
|
||||
invariant(
|
||||
context.isInAParentText,
|
||||
'RawText "%s" must be wrapped in an explicit <Text> component.',
|
||||
this._stringText
|
||||
);
|
||||
this._nativeParent = nativeParent;
|
||||
var tag = ReactNativeTagHandles.allocateTag();
|
||||
this._rootNodeID = tag;
|
||||
var nativeTopRootTag = nativeContainerInfo._tag;
|
||||
UIManager.createView(
|
||||
tag,
|
||||
'RCTRawText',
|
||||
nativeTopRootTag,
|
||||
{text: this._stringText}
|
||||
);
|
||||
|
||||
ReactNativeComponentTree.precacheNode(this, tag);
|
||||
|
||||
return tag;
|
||||
},
|
||||
|
||||
getNativeNode: function() {
|
||||
return this._rootNodeID;
|
||||
},
|
||||
|
||||
receiveComponent: function(nextText, transaction, context) {
|
||||
if (nextText !== this._currentElement) {
|
||||
this._currentElement = nextText;
|
||||
var nextStringText = '' + nextText;
|
||||
if (nextStringText !== this._stringText) {
|
||||
this._stringText = nextStringText;
|
||||
UIManager.updateView(
|
||||
this._rootNodeID,
|
||||
'RCTRawText',
|
||||
{text: this._stringText}
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
unmountComponent: function() {
|
||||
ReactNativeComponentTree.uncacheNode(this);
|
||||
this._currentElement = null;
|
||||
this._stringText = null;
|
||||
this._rootNodeID = null;
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
module.exports = ReactNativeTextComponent;
|
||||
126
src/renderers/native/ReactNative/ReactNativeTreeTraversal.js
Normal file
126
src/renderers/native/ReactNative/ReactNativeTreeTraversal.js
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Copyright 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNativeTreeTraversal
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Same as ReactDOMTreeTraversal without the invariants.
|
||||
|
||||
/**
|
||||
* Return the lowest common ancestor of A and B, or null if they are in
|
||||
* different trees.
|
||||
*/
|
||||
function getLowestCommonAncestor(instA, instB) {
|
||||
var depthA = 0;
|
||||
for (var tempA = instA; tempA; tempA = tempA._nativeParent) {
|
||||
depthA++;
|
||||
}
|
||||
var depthB = 0;
|
||||
for (var tempB = instB; tempB; tempB = tempB._nativeParent) {
|
||||
depthB++;
|
||||
}
|
||||
|
||||
// If A is deeper, crawl up.
|
||||
while (depthA - depthB > 0) {
|
||||
instA = instA._nativeParent;
|
||||
depthA--;
|
||||
}
|
||||
|
||||
// If B is deeper, crawl up.
|
||||
while (depthB - depthA > 0) {
|
||||
instB = instB._nativeParent;
|
||||
depthB--;
|
||||
}
|
||||
|
||||
// Walk in lockstep until we find a match.
|
||||
var depth = depthA;
|
||||
while (depth--) {
|
||||
if (instA === instB) {
|
||||
return instA;
|
||||
}
|
||||
instA = instA._nativeParent;
|
||||
instB = instB._nativeParent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if A is an ancestor of B.
|
||||
*/
|
||||
function isAncestor(instA, instB) {
|
||||
while (instB) {
|
||||
if (instB === instA) {
|
||||
return true;
|
||||
}
|
||||
instB = instB._nativeParent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the parent instance of the passed-in instance.
|
||||
*/
|
||||
function getParentInstance(inst) {
|
||||
return inst._nativeParent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates the traversal of a two-phase, capture/bubble event dispatch.
|
||||
*/
|
||||
function traverseTwoPhase(inst, fn, arg) {
|
||||
var path = [];
|
||||
while (inst) {
|
||||
path.push(inst);
|
||||
inst = inst._nativeParent;
|
||||
}
|
||||
var i;
|
||||
for (i = path.length; i-- > 0;) {
|
||||
fn(path[i], false, arg);
|
||||
}
|
||||
for (i = 0; i < path.length; i++) {
|
||||
fn(path[i], true, arg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses the ID hierarchy and invokes the supplied `cb` on any IDs that
|
||||
* should would receive a `mouseEnter` or `mouseLeave` event.
|
||||
*
|
||||
* Does not invoke the callback on the nearest common ancestor because nothing
|
||||
* "entered" or "left" that element.
|
||||
*/
|
||||
function traverseEnterLeave(from, to, fn, argFrom, argTo) {
|
||||
var common = from && to ? getLowestCommonAncestor(from, to) : null;
|
||||
var pathFrom = [];
|
||||
while (from && from !== common) {
|
||||
pathFrom.push(from);
|
||||
from = from._nativeParent;
|
||||
}
|
||||
var pathTo = [];
|
||||
while (to && to !== common) {
|
||||
pathTo.push(to);
|
||||
to = to._nativeParent;
|
||||
}
|
||||
var i;
|
||||
for (i = 0; i < pathFrom.length; i++) {
|
||||
fn(pathFrom[i], true, argFrom);
|
||||
}
|
||||
for (i = pathTo.length; i-- > 0;) {
|
||||
fn(pathTo[i], false, argTo);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isAncestor: isAncestor,
|
||||
getLowestCommonAncestor: getLowestCommonAncestor,
|
||||
getParentInstance: getParentInstance,
|
||||
traverseTwoPhase: traverseTwoPhase,
|
||||
traverseEnterLeave: traverseEnterLeave,
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Noop
|
||||
|
||||
// TODO: Move all initialization callers back into react-native
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// TODO: Figure out a way to drop this dependency
|
||||
|
||||
var InteractionManager = {};
|
||||
|
||||
module.exports = InteractionManager;
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Noop
|
||||
|
||||
// TODO: Move all initialization callers back into react-native
|
||||
16
src/renderers/native/ReactNative/__mocks__/Platform.js
Normal file
16
src/renderers/native/ReactNative/__mocks__/Platform.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Mock of the Native Hooks
|
||||
|
||||
var Platform = {};
|
||||
|
||||
module.exports = Platform;
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Noop
|
||||
|
||||
// TODO: Move all initialization callers back into react-native
|
||||
14
src/renderers/native/ReactNative/__mocks__/RCTLog.js
Normal file
14
src/renderers/native/ReactNative/__mocks__/RCTLog.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Noop
|
||||
|
||||
// TODO: Move all initialization callers back into react-native
|
||||
17
src/renderers/native/ReactNative/__mocks__/TextInputState.js
Normal file
17
src/renderers/native/ReactNative/__mocks__/TextInputState.js
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Mock of the Native Hooks
|
||||
// TODO: Should this move into the components themselves? E.g. focusable
|
||||
|
||||
var TextInputState = {};
|
||||
|
||||
module.exports = TextInputState;
|
||||
21
src/renderers/native/ReactNative/__mocks__/UIManager.js
Normal file
21
src/renderers/native/ReactNative/__mocks__/UIManager.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Mock of the Native Hooks
|
||||
|
||||
var RCTUIManager = {
|
||||
createView: jest.genMockFunction(),
|
||||
setChildren: jest.genMockFunction(),
|
||||
manageChildren: jest.genMockFunction(),
|
||||
updateView: jest.genMockFunction(),
|
||||
};
|
||||
|
||||
module.exports = RCTUIManager;
|
||||
63
src/renderers/native/ReactNative/__mocks__/deepDiffer.js
Normal file
63
src/renderers/native/ReactNative/__mocks__/deepDiffer.js
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// TODO: Move deepDiffer into react
|
||||
|
||||
var deepDiffer = function(one: any, two: any): bool {
|
||||
if (one === two) {
|
||||
// Short circuit on identical object references instead of traversing them.
|
||||
return false;
|
||||
}
|
||||
if ((typeof one === 'function') && (typeof two === 'function')) {
|
||||
// We consider all functions equal
|
||||
return false;
|
||||
}
|
||||
if ((typeof one !== 'object') || (one === null)) {
|
||||
// Primitives can be directly compared
|
||||
return one !== two;
|
||||
}
|
||||
if ((typeof two !== 'object') || (two === null)) {
|
||||
// We know they are different because the previous case would have triggered
|
||||
// otherwise.
|
||||
return true;
|
||||
}
|
||||
if (one.constructor !== two.constructor) {
|
||||
return true;
|
||||
}
|
||||
if (Array.isArray(one)) {
|
||||
// We know two is also an array because the constructors are equal
|
||||
var len = one.length;
|
||||
if (two.length !== len) {
|
||||
return true;
|
||||
}
|
||||
for (var ii = 0; ii < len; ii++) {
|
||||
if (deepDiffer(one[ii], two[ii])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var key in one) {
|
||||
if (deepDiffer(one[key], two[key])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (var twoKey in two) {
|
||||
// The only case we haven't checked yet is keys that are in two but aren't
|
||||
// in one, which means they are different.
|
||||
if (one[twoKey] === undefined && two[twoKey] !== undefined) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
module.exports = deepDiffer;
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// TODO: move into react or fbjs
|
||||
|
||||
var deepFreezeAndThrowOnMutationInDev = function() { };
|
||||
|
||||
module.exports = deepFreezeAndThrowOnMutationInDev;
|
||||
16
src/renderers/native/ReactNative/__mocks__/flattenStyle.js
Normal file
16
src/renderers/native/ReactNative/__mocks__/flattenStyle.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// TODO: Move flattenStyle into react
|
||||
|
||||
var flattenStyle = function() { };
|
||||
|
||||
module.exports = flattenStyle;
|
||||
18
src/renderers/native/ReactNative/__mocks__/merge.js
Normal file
18
src/renderers/native/ReactNative/__mocks__/merge.js
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// TODO: Replace all callers with spread
|
||||
|
||||
var merge = function(a, b) {
|
||||
return {...a, ...b};
|
||||
};
|
||||
|
||||
module.exports = merge;
|
||||
@@ -0,0 +1,246 @@
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
jest.dontMock('ReactNativeAttributePayload');
|
||||
jest.dontMock('ReactNativePropRegistry');
|
||||
// jest.dontMock('deepDiffer');
|
||||
// jest.dontMock('flattenStyle');
|
||||
|
||||
var ReactNativeAttributePayload = require('ReactNativeAttributePayload');
|
||||
var ReactNativePropRegistry = require('ReactNativePropRegistry');
|
||||
|
||||
var diff = ReactNativeAttributePayload.diff;
|
||||
|
||||
describe('ReactNativeAttributePayload', function() {
|
||||
|
||||
it('should work with simple example', () => {
|
||||
expect(diff(
|
||||
{a: 1, c: 3},
|
||||
{b: 2, c: 3},
|
||||
{a: true, b: true}
|
||||
)).toEqual({a: null, b: 2});
|
||||
});
|
||||
|
||||
it('should skip fields that are equal', () => {
|
||||
expect(diff(
|
||||
{a: 1, b: 'two', c: true, d: false, e: undefined, f: 0},
|
||||
{a: 1, b: 'two', c: true, d: false, e: undefined, f: 0},
|
||||
{a: true, b: true, c: true, d: true, e: true, f: true}
|
||||
)).toEqual(null);
|
||||
});
|
||||
|
||||
it('should remove fields', () => {
|
||||
expect(diff(
|
||||
{a: 1},
|
||||
{},
|
||||
{a: true}
|
||||
)).toEqual({a: null});
|
||||
});
|
||||
|
||||
it('should remove fields that are set to undefined', () => {
|
||||
expect(diff(
|
||||
{a: 1},
|
||||
{a: undefined},
|
||||
{a: true}
|
||||
)).toEqual({a: null});
|
||||
});
|
||||
|
||||
it('should ignore invalid fields', () => {
|
||||
expect(diff(
|
||||
{a: 1},
|
||||
{b: 2},
|
||||
{}
|
||||
)).toEqual(null);
|
||||
});
|
||||
|
||||
it('should use the diff attribute', () => {
|
||||
var diffA = jest.genMockFunction().mockImpl((a, b) => true);
|
||||
var diffB = jest.genMockFunction().mockImpl((a, b) => false);
|
||||
expect(diff(
|
||||
{a: [1], b: [3]},
|
||||
{a: [2], b: [4]},
|
||||
{a: {diff: diffA}, b: {diff: diffB}}
|
||||
)).toEqual({a: [2]});
|
||||
expect(diffA).toBeCalledWith([1], [2]);
|
||||
expect(diffB).toBeCalledWith([3], [4]);
|
||||
});
|
||||
|
||||
it('should not use the diff attribute on addition/removal', () => {
|
||||
var diffA = jest.genMockFunction();
|
||||
var diffB = jest.genMockFunction();
|
||||
expect(diff(
|
||||
{a: [1]},
|
||||
{b: [2]},
|
||||
{a: {diff: diffA}, b: {diff: diffB}}
|
||||
)).toEqual({a: null, b: [2]});
|
||||
expect(diffA).not.toBeCalled();
|
||||
expect(diffB).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should do deep diffs of Objects by default', () => {
|
||||
expect(diff(
|
||||
{a: [1], b: {k: [3, 4]}, c: {k: [4, 4]} },
|
||||
{a: [2], b: {k: [3, 4]}, c: {k: [4, 5]} },
|
||||
{a: true, b: true, c: true}
|
||||
)).toEqual({a: [2], c: {k: [4, 5]}});
|
||||
});
|
||||
|
||||
it('should work with undefined styles', () => {
|
||||
expect(diff(
|
||||
{ style: { a: '#ffffff', b: 1 } },
|
||||
{ style: undefined },
|
||||
{ style: { b: true } }
|
||||
)).toEqual({ b: null });
|
||||
expect(diff(
|
||||
{ style: undefined },
|
||||
{ style: { a: '#ffffff', b: 1 } },
|
||||
{ style: { b: true } }
|
||||
)).toEqual({ b: 1 });
|
||||
expect(diff(
|
||||
{ style: undefined },
|
||||
{ style: undefined },
|
||||
{ style: { b: true } }
|
||||
)).toEqual(null);
|
||||
});
|
||||
|
||||
it('should work with empty styles', () => {
|
||||
expect(diff(
|
||||
{a: 1, c: 3},
|
||||
{},
|
||||
{a: true, b: true}
|
||||
)).toEqual({a: null});
|
||||
expect(diff(
|
||||
{},
|
||||
{a: 1, c: 3},
|
||||
{a: true, b: true}
|
||||
)).toEqual({a: 1});
|
||||
expect(diff(
|
||||
{},
|
||||
{},
|
||||
{a: true, b: true}
|
||||
)).toEqual(null);
|
||||
});
|
||||
|
||||
it('should flatten nested styles and predefined styles', () => {
|
||||
var validStyleAttribute = { someStyle: { foo: true, bar: true } };
|
||||
|
||||
expect(diff(
|
||||
{},
|
||||
{ someStyle: [{ foo: 1 }, { bar: 2 }]},
|
||||
validStyleAttribute
|
||||
)).toEqual({ foo: 1, bar: 2 });
|
||||
|
||||
expect(diff(
|
||||
{ someStyle: [{ foo: 1 }, { bar: 2 }]},
|
||||
{},
|
||||
validStyleAttribute
|
||||
)).toEqual({ foo: null, bar: null });
|
||||
|
||||
var barStyle = ReactNativePropRegistry.register({
|
||||
bar: 3,
|
||||
});
|
||||
|
||||
expect(diff(
|
||||
{},
|
||||
{ someStyle: [[{ foo: 1 }, { foo: 2 }], barStyle]},
|
||||
validStyleAttribute
|
||||
)).toEqual({ foo: 2, bar: 3 });
|
||||
});
|
||||
|
||||
it('should reset a value to a previous if it is removed', () => {
|
||||
var validStyleAttribute = { someStyle: { foo: true, bar: true } };
|
||||
|
||||
expect(diff(
|
||||
{ someStyle: [{ foo: 1 }, { foo: 3 }]},
|
||||
{ someStyle: [{ foo: 1 }, { bar: 2 }]},
|
||||
validStyleAttribute
|
||||
)).toEqual({ foo: 1, bar: 2 });
|
||||
});
|
||||
|
||||
it('should not clear removed props if they are still in another slot', () => {
|
||||
var validStyleAttribute = { someStyle: { foo: true, bar: true } };
|
||||
|
||||
expect(diff(
|
||||
{ someStyle: [{}, { foo: 3, bar: 2 }]},
|
||||
{ someStyle: [{ foo: 3 }, { bar: 2 }]},
|
||||
validStyleAttribute
|
||||
)).toEqual({ foo: 3 }); // this should ideally be null. heuristic tradeoff.
|
||||
|
||||
expect(diff(
|
||||
{ someStyle: [{}, { foo: 3, bar: 2 }]},
|
||||
{ someStyle: [{ foo: 1, bar: 1 }, { bar: 2 }]},
|
||||
validStyleAttribute
|
||||
)).toEqual({ bar: 2, foo: 1 });
|
||||
});
|
||||
|
||||
it('should clear a prop if a later style is explicit null/undefined', () => {
|
||||
var validStyleAttribute = { someStyle: { foo: true, bar: true } };
|
||||
expect(diff(
|
||||
{ someStyle: [{}, { foo: 3, bar: 2 }]},
|
||||
{ someStyle: [{ foo: 1 }, { bar: 2, foo: null }]},
|
||||
validStyleAttribute
|
||||
)).toEqual({ foo: null });
|
||||
|
||||
expect(diff(
|
||||
{ someStyle: [{ foo: 3 }, { foo: null, bar: 2 }]},
|
||||
{ someStyle: [{ foo: null }, { bar: 2 }]},
|
||||
validStyleAttribute
|
||||
)).toEqual({ foo: null });
|
||||
|
||||
expect(diff(
|
||||
{ someStyle: [{ foo: 1 }, { foo: null }]},
|
||||
{ someStyle: [{ foo: 2 }, { foo: null }]},
|
||||
validStyleAttribute
|
||||
)).toEqual({ foo: null }); // this should ideally be null. heuristic.
|
||||
|
||||
// Test the same case with object equality because an early bailout doesn't
|
||||
// work in this case.
|
||||
var fooObj = { foo: 3 };
|
||||
expect(diff(
|
||||
{ someStyle: [{ foo: 1 }, fooObj]},
|
||||
{ someStyle: [{ foo: 2 }, fooObj]},
|
||||
validStyleAttribute
|
||||
)).toEqual({ foo: 3 }); // this should ideally be null. heuristic.
|
||||
|
||||
expect(diff(
|
||||
{ someStyle: [{ foo: 1 }, { foo: 3 }]},
|
||||
{ someStyle: [{ foo: 2 }, { foo: undefined }]},
|
||||
validStyleAttribute
|
||||
)).toEqual({ foo: null }); // this should ideally be null. heuristic.
|
||||
});
|
||||
|
||||
// Function properties are just markers to native that events should be sent.
|
||||
it('should convert functions to booleans', () => {
|
||||
// Note that if the property changes from one function to another, we don't
|
||||
// need to send an update.
|
||||
expect(diff(
|
||||
{
|
||||
a: function() {
|
||||
return 1;
|
||||
},
|
||||
b: function() {
|
||||
return 2;
|
||||
},
|
||||
c: 3,
|
||||
},
|
||||
{
|
||||
b: function() {
|
||||
return 9;
|
||||
},
|
||||
c: function() {
|
||||
return 3;
|
||||
},
|
||||
},
|
||||
{a: true, b: true, c: true}
|
||||
)).toEqual({a: null, c: true});
|
||||
});
|
||||
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user