Compare commits
113 Commits
17.0.0-dev
...
v15.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fef495942a | ||
|
|
b7d480986a | ||
|
|
da6a30b022 | ||
|
|
bca912f91e | ||
|
|
5b77fff586 | ||
|
|
95db5bab42 | ||
|
|
b4c1356d18 | ||
|
|
668dd47700 | ||
|
|
d9bcda01db | ||
|
|
8c50a04455 | ||
|
|
80b849e2a0 | ||
|
|
e15a7d1f7e | ||
|
|
7da8884581 | ||
|
|
871523c8f4 | ||
|
|
09172b112f | ||
|
|
3adc4a6e0e | ||
|
|
b29bf7515b | ||
|
|
cba62feeec | ||
|
|
09c6d53e64 | ||
|
|
78aa706491 | ||
|
|
f50d542ff7 | ||
|
|
583d7205c6 | ||
|
|
1f1dba92a8 | ||
|
|
258e591e45 | ||
|
|
42d27cd152 | ||
|
|
d8d6c7a07b | ||
|
|
4f0163fd38 | ||
|
|
0244879c8f | ||
|
|
743e4c6231 | ||
|
|
c3d99b5292 | ||
|
|
d1f519e0cd | ||
|
|
ff3cec5beb | ||
|
|
b0deadc05d | ||
|
|
ced4ef9ad6 | ||
|
|
ca19ae5ad0 | ||
|
|
9460263e41 | ||
|
|
197ecabb9a | ||
|
|
ee459684f1 | ||
|
|
6516c72ef1 | ||
|
|
9d022818e1 | ||
|
|
b9ae9f5f38 | ||
|
|
b8ac9e1597 | ||
|
|
9537b240de | ||
|
|
9c6de78844 | ||
|
|
6bb4fea31d | ||
|
|
97849a08cd | ||
|
|
2f435912d8 | ||
|
|
d737dc6dd2 | ||
|
|
1b22f12acd | ||
|
|
69bb9e3c8c | ||
|
|
772b5f9a5a | ||
|
|
64acaca8ee | ||
|
|
0008beb1fb | ||
|
|
846b5ea252 | ||
|
|
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" \
|
||||
|
||||
143
CHANGELOG.md
143
CHANGELOG.md
@@ -1,3 +1,144 @@
|
||||
## 15.1.0 (May 20, 2016)
|
||||
|
||||
### React
|
||||
- Ensure we're using the latest `object-assign`, which has protection against a non-spec-compliant native `Object.assign`. ([@zpao](https://github.com/zpao) in [#6681](https://github.com/facebook/react/pull/6681))
|
||||
- Add a new warning to communicate that `props` objects passed to `createElement` must be plain objects. ([@richardscarrott](https://github.com/richardscarrott) in [#6134](https://github.com/facebook/react/pull/6134))
|
||||
- Fix a batching bug resulting in some lifecycle methods incorrectly being called multiple times. ([@spicyj](https://github.com/spicyj) in [#6650](https://github.com/facebook/react/pull/6650))
|
||||
|
||||
### React DOM
|
||||
- Fix regression in custom elements support. ([@jscissr](https://github.com/jscissr) in [#6570](https://github.com/facebook/react/pull/6570))
|
||||
- Stop incorrectly warning about using `onScroll` event handler with server rendering. ([@Aweary](https://github.com/Aweary) in [#6678](https://github.com/facebook/react/pull/6678))
|
||||
- Fix grammar in the controlled input warning. ([@jakeboone02](https://github.com/jakeboone02) in [#6657](https://github.com/facebook/react/pull/6657))
|
||||
- Fix issue preventing `<object>` nodes from being able to read `<param>` nodes in IE. ([@syranide](https://github.com/syranide) in [#6691](https://github.com/facebook/react/pull/6691))
|
||||
- Fix issue resulting in crash when using experimental error boundaries with server rendering. ([@jimfb](https://github.com/jimfb) in [#6694](https://github.com/facebook/react/pull/6694))
|
||||
- Add additional information to the controlled input warning. ([@borisyankov](https://github.com/borisyankov) in [#6341](https://github.com/facebook/react/pull/6341))
|
||||
|
||||
### React Perf Add-on
|
||||
- Completely rewritten to collect data more accurately and to be easier to maintain. ([@gaearon](https://github.com/gaearon) in [#6647](https://github.com/facebook/react/pull/6647), [#6046](https://github.com/facebook/react/pull/6046))
|
||||
|
||||
### React Native Renderer
|
||||
- Remove some special cases for platform specific branching. ([@sebmarkbage](https://github.com/sebmarkbage) in [#6660](https://github.com/facebook/react/pull/6660))
|
||||
- Remove use of `merge` utility. ([@sebmarkbage](https://github.com/sebmarkbage) in [#6634](https://github.com/facebook/react/pull/6634))
|
||||
- Renamed some modules to better indicate usage ([@javache](https://github.com/javache) in [#6643](https://github.com/facebook/react/pull/6643))
|
||||
|
||||
|
||||
## 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 +229,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.1.0.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.1.0.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.1.0.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.2
|
||||
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: dedPNF++oHBwT++5v2VMeXSzyNkqJd7tbc3ic+wN3l0u/NEBcMNuf9a7knYgwTco
|
||||
prod: 1kql9Cm3anPpO0n0a4T7keJt+N4pixvMU33Bk69kZi1WkN6gbq1EVZdfZLjqwmFq
|
||||
addons_dev: b7jdy0/iqeUofUONQyRN/Y3DO/h61sL56BgMXJzfokC2mtsi2pJGhQekw6QqI/71
|
||||
addons_prod: 1J/3e0bGfmFbeuWDRmyBoBOImeFnDn03OKWsXiWVOThql8Yf1qXaim7NrAqL4RHi
|
||||
dom_dev: kQ+9o036IWQT7tKVxA8siLqHR0DekC3YXbkA7RsFQHbuqQutOmb3cOsTQ1WFtMhW
|
||||
dom_prod: q14dOJErroITWJ+DFXug03tOxuZ4fm7u5BkcbboXFWJNvDGPepPWXUuIphvvAsAl
|
||||
dom_server_dev: WiRnBunlOqqLKTWNe+788pJ0QLFIXXktwBnqNV2OggmTROfZJvn1WfrZyh0xhnja
|
||||
dom_server_prod: wkSOmc5KBqBSFy+CAVk0ehSoBEMqzpKj4lzcoKLBchNzTIwCAZ3Pe+LX73kyNNhz
|
||||
|
||||
@@ -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>
|
||||
29
docs/docs/01-why-react.ru-RU.md
Normal file
29
docs/docs/01-why-react.ru-RU.md
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
id: why-react-ru-RU
|
||||
title: Почему именно React?
|
||||
permalink: why-react-ru-RU.html
|
||||
next: displaying-data-ru-RU.html
|
||||
---
|
||||
React — библиотека JavaScript для создания интерфейсов от команд Facebook и Instagram. Многие ассоциируют React с понятием **View** в паттерне **[MVC](https://ru.wikipedia.org/wiki/Model-View-Controller)**.
|
||||
|
||||
Мы делали React, чтобы решить одну важную задачу: **создавать действительно большие приложения с постоянно меняющимися данными**.
|
||||
|
||||
## Простота
|
||||
|
||||
С React вы всегда точно знаете как будет выглядеть ваше приложение, ведь как только изменятся данные, он мгновенно отобразит эти изменения в интерфейсе.
|
||||
|
||||
## Декларативность
|
||||
|
||||
Как только состояние приложения изменится, React будто нажимает кнопку "обновить" и точно знает, какие части интерфейса надо поменять, а какие нет. Никаких дополнительных инструкций и команд, React сам отслеживает изменения данных и реагирует на них.
|
||||
|
||||
## Создание компонентов, как строительных блоков приложения
|
||||
|
||||
По сути, разработка на React целиком состоит в создании таких компонентов. С React вы *только* тем и занимаетесь, что пишете новые компоненты, те самые строительные блоки, из которых будет строиться приложение. А поскольку они хорошо инскапсулированы, их удобно использовать повторно даже в других проектах, плюс такой код проще тестировать.
|
||||
|
||||
## Уделите этому 5 минут
|
||||
|
||||
React бросает вызов многим устоявшимся идеям и правилам, и на первый взгляд, некоторые из его идей выглядят по меньшей мере странными. [Уделите этому 5 минут](https://signalvnoise.com/posts/3124-give-it-five-minutes) пока читаете эту статью; эти безумные идеи нашли свое применение при создании тысяч компонентов не только для Facebook и Instagram, но и в других крупных проектах.
|
||||
|
||||
## Узнай больше
|
||||
|
||||
Вы можете больше узнать о причинах создания React [отсюда](/react/blog/2013/06/05/why-react.html).
|
||||
29
docs/docs/01-why-react.zh-TW.md
Normal file
29
docs/docs/01-why-react.zh-TW.md
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
id: why-react-zh-TW
|
||||
title: Why React?
|
||||
permalink: why-react-zh-TW.html
|
||||
next: displaying-data.html
|
||||
---
|
||||
React是Facebook和Instagram用來建立使用者介面的JavaScript函式庫. 很多人認為React就是處理 **[MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)**架構中 **V** 的部份.
|
||||
|
||||
我們建造React用來解決一個問題: **開發資料能隨時間頻繁更新的大型應用程式**.
|
||||
|
||||
## 簡單(Simple)
|
||||
|
||||
簡單意味著你所開發的應用程式外觀任何一部分都要能即時呈現, 並且當資料有所變動時React能自動管理所有UI的更新.
|
||||
|
||||
## 陳述(Declarative)
|
||||
|
||||
當資料改變時, React概念上就像是點擊了 "刷新" 的按鈕, 並且知道只需更新有改變的部份.
|
||||
|
||||
## 建立可組合的元件(Composable Components)
|
||||
|
||||
React就是在建造可重用的元件(Components). 事實上, 當你使用React時 *唯一* 在做的事就是建立元件(Components). 由於它們封裝性高,元件使得程式碼能夠易於重複使用, 測試, 並且容易做到讓關注點分離(separation of concerns easy).
|
||||
|
||||
## 指引(Give It Five Minutes)
|
||||
|
||||
React挑戰了許多傳統的觀念, 第一次乍看之下這些構想可能看起來有點瘋狂. [Give it five minutes](https://signalvnoise.com/posts/3124-give-it-five-minutes) 而當閱讀完這篇指引; 這些瘋狂的構想在Facebook和Instagram裡裡外外建立了數以千計的元件(components)之後被證明是可實行的.
|
||||
|
||||
## 更多學習資源
|
||||
|
||||
從這裡你能學習到更多建造React背後的動機 [this blog post](/react/blog/2013/06/05/why-react.html).
|
||||
124
docs/docs/02-displaying-data.ru-RU.md
Normal file
124
docs/docs/02-displaying-data.ru-RU.md
Normal file
@@ -0,0 +1,124 @@
|
||||
---
|
||||
id: displaying-data-ru-RU
|
||||
title: Отображение данных
|
||||
permalink: displaying-data-ru-RU.html
|
||||
prev: why-react-ru-RU.html
|
||||
next: jsx-in-depth.html
|
||||
---
|
||||
|
||||
Главная задача интерфейса — это отображать данные. React делает это легко и обновляет интерфейс сразу, как только изменятся данные.
|
||||
|
||||
## Начало
|
||||
|
||||
Давайте рассмотрим простой пример. Создайте файл `hello-react.html` со следующим текстом:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Hello React</title>
|
||||
<script src="https://fb.me/react-{{site.react_version}}.js"></script>
|
||||
<script src="https://fb.me/react-dom-{{site.react_version}}.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">
|
||||
|
||||
// ** Ваш код будет здесь! **
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Добавим в этот шаблон немного JavaScript. Замените комментарий на следующий JSX-код:
|
||||
|
||||
```javascript
|
||||
var HelloWorld = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<p>
|
||||
Hello, <input type="text" placeholder="Your name here" />!
|
||||
It is {this.props.date.toTimeString()}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
setInterval(function() {
|
||||
ReactDOM.render(
|
||||
<HelloWorld date={new Date()} />,
|
||||
document.getElementById('example')
|
||||
);
|
||||
}, 500);
|
||||
```
|
||||
|
||||
## Реактивные обновления
|
||||
|
||||
Откройте `hello-react.html` в браузере и введите в текстовое поле свое имя. Что происходит со страницей? Каждые полсекунды обновляется время, остальные же части страницы остаются без изменений. Обратите внимание, что мы не написали ни строчки кода, чтобы управлять этим поведением. React сам отлично понимает что надо делать и обновляет элементы на странице по мере необходимости.
|
||||
|
||||
Суть в том, что React не меняет DOM-дерево до тех пор, пока это не потребуется. **Чтобы отразить изменения, React использует быстрое внутреннее представление DOM-дерева и просчитывает как его изменить наиболее эффективно**.
|
||||
|
||||
Передаваемые в компонент данные называются `props` — сокращенно от "properties". В JSX коде они передаются как атрибуты компонента. Считайте, что компонент получает `props` только для чтения. **Никогда не перезаписывайте значения `this.props` внутри компонента.**
|
||||
|
||||
## Компоненты как функции
|
||||
|
||||
Компоненты React — довольно простые сущности. Можно считать их обыкновенными функциями, которые принимают на входе `props` и `state` (см. далее) и возвращают HTML. Если помнить об этом, то компоненты становятся простыми для понимания.
|
||||
|
||||
> Замечание:
|
||||
>
|
||||
> **Есть одно ограничение**: Компоненты React умеют возвращать только один узел. Если вам надо вернуть сразу несколько, они *должны* быть обернуты в один корневой узел.
|
||||
|
||||
## Синтаксис JSX
|
||||
|
||||
Мы убеждены, что компоненты — самый подходящий способ разделения ответственностей, гораздо более удобный чем "шаблоны" и "вынесение логики на страницу". Мы считаем, что разметка и код, который её генерирует, неотделимы друг от друга. Плюс, логика на странице часто бывает запутанной, и использование шаблонизаторов, чтобы описать её, только затрудняет работу.
|
||||
|
||||
Мы решили, что лучшим вариантом будет генерировать HTML и деревья компонентов прямо из JS кода. Так вы сможете зайдействовать всю выразительную мощь современного языка программирования для создания интерфейсов.
|
||||
|
||||
А чтобы упростить создание узлов дерева, мы ввели **опциональный** HTML-подобный синтаксис.
|
||||
|
||||
**JSX позволяет вам создавать JavaScript объекты используя синтаксис HTML**. Для генерации ссылки в React вы напишете на чистом JavaScript:
|
||||
|
||||
`React.createElement('a', {href: 'https://facebook.github.io/react/'}, 'Hello!')`
|
||||
|
||||
С JSX это станет:
|
||||
|
||||
`<a href="https://facebook.github.io/react/">Hello!</a>`
|
||||
|
||||
Мы установили, что с JSX создавать React приложения проще, и дизайнеров как правило устраивает его синтаксис. Но у разных людей разные предпочтения, поэтому стоит сказать, что **JSX необязателен при работе с React.**
|
||||
|
||||
JSX сам по себе очень прост. Чтобы узнать о нем больше, почитайте [подробно про JSX](/react/docs/jsx-in-depth.html). Или можете попробовать его в [Babel REPL](https://babeljs.io/repl/).
|
||||
|
||||
JSX похож на HTML, но но имеет существенные отличия. Почитайте про [подводные камни JSX](/react/docs/jsx-gotchas.html), чтобы понять их ключевые различия.
|
||||
|
||||
[Babel предлагает несколько способов начать работу с JSX](http://babeljs.io/docs/setup/), от консольных утилит до интеграций с Ruby on Rails. Выберите тот инструмент, который лучше всего вам подходит.
|
||||
|
||||
## React без использования JSX
|
||||
|
||||
JSX полностью опционален; вам совсем необязательно использовать его вместе с React. Вы можете создавать React-элементы на чистом JavaScript используя функцию `React.createElement`, которая принимает имя тега или компонента, объект со свойствами, и набор необязательных дочерних элементов.
|
||||
|
||||
```javascript
|
||||
var child1 = React.createElement('li', null, 'First Text Content');
|
||||
var child2 = React.createElement('li', null, 'Second Text Content');
|
||||
var root = React.createElement('ul', { className: 'my-list' }, child1, child2);
|
||||
ReactDOM.render(root, document.getElementById('example'));
|
||||
```
|
||||
|
||||
Для удобства, вы можете создать сокращенные фабричные функции, чтобы создавать React-элементы из ваших собственных компонентов.
|
||||
|
||||
```javascript
|
||||
var Factory = React.createFactory(ComponentClass);
|
||||
...
|
||||
var root = Factory({ custom: 'prop' });
|
||||
ReactDOM.render(root, document.getElementById('example'));
|
||||
```
|
||||
|
||||
А для базовых HTML тегов в React уже есть встроенные фабрики:
|
||||
|
||||
```javascript
|
||||
var root = React.DOM.ul({ className: 'my-list' },
|
||||
React.DOM.li(null, 'Text Content')
|
||||
);
|
||||
```
|
||||
@@ -40,10 +40,10 @@ A safer alternative is to find the [unicode number corresponding to the entity](
|
||||
<div>{'First ' + String.fromCharCode(183) + ' Second'}</div>
|
||||
```
|
||||
|
||||
You can use mixed arrays with strings and JSX elements.
|
||||
You can use mixed arrays with strings and JSX elements. Each JSX element in the array needs a unique key.
|
||||
|
||||
```javascript
|
||||
<div>{['First ', <span>·</span>, ' Second']}</div>
|
||||
<div>{['First ', <span key="middot">·</span>, ' Second']}</div>
|
||||
```
|
||||
|
||||
As a last resort, you always have the ability to [insert raw HTML](/react/tips/dangerously-set-inner-html.html).
|
||||
|
||||
78
docs/docs/03-interactivity-and-dynamic-uis.ru-RU.md
Normal file
78
docs/docs/03-interactivity-and-dynamic-uis.ru-RU.md
Normal file
@@ -0,0 +1,78 @@
|
||||
---
|
||||
id: interactivity-and-dynamic-uis-ru-RU
|
||||
title: Интерактивные и динамические интерфейсы
|
||||
permalink: interactivity-and-dynamic-uis-ru-RU.html
|
||||
prev: jsx-gotchas.html
|
||||
next: multiple-components.html
|
||||
---
|
||||
|
||||
Вы уже знаете [как показывать данные](/react/docs/displaying-data.html) с React. Теперь давайте добавим в наши интферфейсы немного интерактивности.
|
||||
|
||||
## Простой пример
|
||||
|
||||
```javascript
|
||||
var LikeButton = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {liked: false};
|
||||
},
|
||||
handleClick: function(event) {
|
||||
this.setState({liked: !this.state.liked});
|
||||
},
|
||||
render: function() {
|
||||
var text = this.state.liked ? 'like' : 'haven\'t liked';
|
||||
return (
|
||||
<p onClick={this.handleClick}>
|
||||
You {text} this. Click to toggle.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ReactDOM.render(
|
||||
<LikeButton />,
|
||||
document.getElementById('example')
|
||||
);
|
||||
```
|
||||
|
||||
## Обработка событий и синтетические события
|
||||
|
||||
С React вы просто передаете функцию-обработчик нужного события как аргумент, почти так же, как делали это в HTML. Благодаря механизму синтетических событий React гарантирует, что все события будут вести себя одинаково во всех браузерах. Другими словами, React знает, как работает всплытие и перехват событий по спецификации. События, которые он передает в ваши обработчики, будут соответствовать [спецификации W3C](http://www.w3.org/TR/DOM-Level-3-Events/), несмотря на то, каким браузером вы пользуетесь.
|
||||
|
||||
## Как это работает: автоматическое связывание и делегирование событий
|
||||
|
||||
Чтобы ваш код был не только понятным, но и быстрым, React делает следующее:
|
||||
|
||||
**Автоматическое связывание:** Когда в JavaScript создаются функции обратного вызова, вам надо привязать метод к тому объекту, на котором он будет вызываться, чтобы значение `this` было корректным. С React привязка метода к компоненту происходит автоматически ([кроме тех случаев, когда вы используете классы ES6](/react/docs/reusable-components.html#no-autobinding)). И делается это с минимальной нагрузкой на процессор и память.
|
||||
|
||||
**Делегирование событий:** На самом же деле, React добавляет обработчики событий не к узлам дерева. Сразу после запуска, React начинает прослушивать все события с самого верхнего уровня, используя единый слушатель. Когда добавляется новый компонент или удаляется старый, обработчики событий просто добавляются или удаляются из памяти React. И когда событие наступает, React уже заранее знает какому из обработчиков его передать. Когда в памяти больше не остается обработчиков, React перестает обрабатывать события. Если хотите узнать о том, почему эта механика так быстро работает, почитайте [отличный пост в блоге David Walsh](http://davidwalsh.name/event-delegate).
|
||||
|
||||
## Компоненты как конечные автоматы
|
||||
|
||||
React считает интерфейсы обыкновенными конечными автоматами. Работать с интерфейсом становится проще, если представлять его как конечный автомат, который меняет состояния и отрисовывает их.
|
||||
|
||||
В React вы просто обновляете состояние компонента, а потом выводите новый интерфейс уже с новыми данными. Все изменения в DOM-дереве React сделает сам, причем наиболее эффективным способом.
|
||||
|
||||
## Как работает состояние
|
||||
|
||||
Чтобы сообщить React о том, что данные изменились, вы вызываете метод `setState(data, callback)`. В этом методе происходит обновление состояния `this.state` новыми данными из `data`, и компонент отрисуется заново. После этого вызывается функция `callback`. Но вы редко будете ей пользоваться, ведь React сам обновляет интерфейс.
|
||||
|
||||
## В каких компонентах хранить состояние?
|
||||
|
||||
Большинство компонентов должны просто брать данные из `props` и отрисовывать их. Но иногда вам надо реагировать на действия пользователя, делать запросы на сервер или просто сделать что-то по таймеру. В таких случаях используйте состояние.
|
||||
|
||||
**Старайтесь делать компоненты без состояния.** Следуя этому правилу, вы будете выносить работу с состоянием с уровня представления в другие, более подходящие места. Тем самым, вы снизите сложность приложения, упрощая его понимание.
|
||||
|
||||
Основной принцип такой: создаются несколько компонентов без состояния, которые формируют дерево. Они будут заниматься только отрисовкой данных. А все данные для них будут у родительского компонента, который будет на вершине этого дерева компонентов. Он и будет передавать данные дочерним узлам через `props`. Этот компонент с общим состоянием содержит в себе всю логику взаимодействия, а дочерние компоненты будут только отрисовывать данные, которые будут у них в `props`.
|
||||
|
||||
## Какие данные *надо* помещать в состояние?
|
||||
|
||||
**Состояние должно содержать данные, которые нужны для обновления интерфейса.** В реальных приложениях такие данные, как правило, незначительны по объему, и могут быть сериализованы в JSON. Когда вы создаете компонент с состоянием, старайтесь поместить в него минимум данных. А уже внутри метода `render()` вычисляйте остальные данные, используя значения из состояния.
|
||||
Со временем вы увидите, что такой подход позволяет создавать более стройные и устойчивые к изменениям приложения. Добавление в состояние лишних данных требует от вас дополнительных затрат на их синхронизацию. Но этого можно избежать, если позволить React делать все эти вычисления за вас.
|
||||
|
||||
## Какие данные *не надо* помещать в состояние?
|
||||
|
||||
Состояние `this.state` должно содержать минимум данных, необходимых для отображения интерфейса. Поэтому не стоит хранить в нем:
|
||||
|
||||
* **Вычисляемые данные:** Не волнуйтесь о данных, которые можно вычислить из состояния. Согласованность данных проще обеспечить, если производить все вычисления в методе `render()`. Например, если в состоянии хранится список элементов, и вам надо вывести его размер в виде строки, напишите `this.state.listItems.length + ' элементов'` в методе `render()`. Это будет правильнее, чем хранить размер списка в состоянии.
|
||||
* **Компоненты React:** Создавайте их в методе `render()`, опираясь на данные из `props` и `state`.
|
||||
* **Значения, повторяющие `props`:** Старайтесь по мере возможности использовать `props` как единственный источник данных. Хранить значения `props` в состоянии допускается, только если вам надо где-то хранить их прошлые значения, ведь `props` могут измениться после отрисовки родительского компонента.
|
||||
@@ -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.
|
||||
|
||||
@@ -166,3 +166,8 @@ To make an uncontrolled component, `defaultValue` is used instead.
|
||||
> Note:
|
||||
>
|
||||
> You can pass an array into the `value` attribute, allowing you to select multiple options in a `select` tag: `<select multiple={true} value={['B', 'C']}>`.
|
||||
|
||||
### Imperative operations
|
||||
|
||||
If you need to imperatively perform an operation, you have to obtain a [reference to the DOM node](/react/docs/more-about-refs.html#the-ref-callback-attribute).
|
||||
For instance, if you want to imperatively submit a form, one approach would be to attach a `ref` to the `form` element and manually call `form.submit()`.
|
||||
|
||||
@@ -64,7 +64,7 @@ or using an ES6 arrow function:
|
||||
|
||||
When attaching a ref to a DOM component like `<div />`, you get the DOM node back; when attaching a ref to a composite component like `<TextInput />`, you'll get the React class instance. In the latter case, you can call methods on that component if any are exposed in its class definition.
|
||||
|
||||
Note that when the referenced component is unmounted and whenever the ref changes, the old ref will be called with `null` as an argument. This prevents memory leaks in the case that the instance is stored, as in the first example. Also note that when writing refs with inline function expressions as in the examples here, React sees a different function object each time so on every update, ref will be called with `null` immediately before it's called with the component instance.
|
||||
Note that when the referenced component is unmounted and whenever the ref changes, the old ref will be called with `null` as an argument. This prevents memory leaks in the case that the instance is stored, as in the second example. Also note that when writing refs with inline function expressions as in the examples here, React sees a different function object each time so on every update, ref will be called with `null` immediately before it's called with the component instance.
|
||||
|
||||
## The ref String Attribute
|
||||
|
||||
|
||||
@@ -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/tutorials/React%20%26%20Webpack.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
|
||||
@@ -138,7 +142,8 @@ Traverse all components in `tree` and accumulate all components where `test(comp
|
||||
|
||||
```javascript
|
||||
array scryRenderedDOMComponentsWithClass(
|
||||
ReactComponent tree, string className
|
||||
ReactComponent tree,
|
||||
string className
|
||||
)
|
||||
```
|
||||
|
||||
@@ -192,7 +197,8 @@ Finds all instances of components with type equal to `componentClass`.
|
||||
|
||||
```javascript
|
||||
ReactComponent findRenderedComponentWithType(
|
||||
ReactComponent tree, function componentClass
|
||||
ReactComponent tree,
|
||||
function componentClass
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@@ -179,10 +179,10 @@ var MediaQuery = React.createClass({
|
||||
var checkMediaQuery = function(){
|
||||
var type = window.matchMedia("(min-width: 1025px)").matches ? 'desktop' : 'mobile';
|
||||
if (type !== this.state.type){
|
||||
this.setState({type:type});
|
||||
this.setState({type:type});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
window.addEventListener('resize', checkMediaQuery);
|
||||
checkMediaQuery();
|
||||
},
|
||||
|
||||
@@ -5,21 +5,21 @@ permalink: context-zh-CN.html
|
||||
prev: advanced-performance-zh-CN.html
|
||||
---
|
||||
|
||||
React最大的优势之一是很容易从你的React组件里跟踪数据流动。当你看着一个组件,你可以很容易准确看出哪个props被传入,这让你的APP很容易推断。
|
||||
React优势之一是你可以很容易地从React组件里跟踪数据流动。当你查看一个组件时,你可以很容易地判断出传入了哪些props,而这也有利于你的App进行逻辑推断。
|
||||
|
||||
偶尔,你想通过组件树传递数据,而不在每一级上手工下传prop,React的 "context" 让你做到这点。
|
||||
有时,你想不通过在每一级组件设置prop的方式来向组件树内传递数据。那么,React的"context"特性可以让你做到这点。
|
||||
|
||||
> 注意:
|
||||
>
|
||||
> Context是一个先进的实验性特性.这个 API 很可能在未来版本变化.
|
||||
> 注意:
|
||||
>
|
||||
> 大多数应用将不会需要用到 context. 尤其是如果你刚开始用React,你很可能不会想用 context.使用 context 将会使你的代码很难理解因为它让数据流不清晰.它类似于在你的应用里使用全局变量传递state.
|
||||
> Context是一个先进的实验性特性,这个API很可能在未来版本变化。
|
||||
>
|
||||
> **如果你必须使用 context ,保守的使用它**
|
||||
> 大多数应用将不会需要用到context。尤其是如果你刚开始用React,你很可能不会想用它。使用context将会使你的代码难以理解,因为它让数据流变得不清晰。它类似于在你的应用里用以传递state的全局变量。
|
||||
>
|
||||
> 不论你正在创建一个应用或者是库,试着分离你对 context 的使用到一个小区域,并尽可能避免直接使用 context API,以便在API变动时容易升级.
|
||||
> **如果你必须使用context,请保守地使用它。**
|
||||
>
|
||||
> 不论你正在创建一个应用或者是库,试着缩小context的使用范围,并尽可能避免直接使用context相关API,以便在API变动时容易升级。
|
||||
|
||||
## 从树里自动传递info
|
||||
## 在组件树内自动传递info
|
||||
|
||||
假设你有一个这样的结构:
|
||||
|
||||
@@ -55,7 +55,7 @@ var MessageList = React.createClass({
|
||||
});
|
||||
```
|
||||
|
||||
在这里例子里,我们手工穿透一个 `color` prop 以便于恰当格式化 `Button` 和 `Message` 组件.主题是一个很好的例子,当你可能想整个子树都可以访问一部分信息时(比如color). 使用 context 我们可以自动传过这个树:
|
||||
在这个例子里,我们手工传递一个叫`color`的prop,以便于给`Button`和`Message`组件添加合适的样式。当你想所有子组件可以访问一部分信息时(比如color),上面的设置主题是一个很好的例子。通过使用context,我们能自动在组件树中传递信息:
|
||||
|
||||
```javascript{2-4,7,18,25-30,33}
|
||||
var Button = React.createClass({
|
||||
@@ -97,13 +97,13 @@ var MessageList = React.createClass({
|
||||
});
|
||||
```
|
||||
|
||||
通过添加 `childContextTypes` 和 `getChildContext` 到 `MessageList` ( context 提供),React下传信息到子树中的任何组件(在这个例子中, `Button`可以由定义 `contextTypes`来访问它).
|
||||
通过添加`childContextTypes`和`getChildContext`到 `MessageList`(context提供者),React会自动下传信息,并且通过定义`contextTypes`,子树中的任何组件(在这个例子中,子组件是`Button`)可以访问`context`。
|
||||
|
||||
如果 `contextTypes` 没有定义,那么 `this.context` 将是一个空对象.
|
||||
如果`contextTypes`没有定义,那么`this.context`将是一个空对象。
|
||||
|
||||
## 父子耦合
|
||||
|
||||
Context 同样可以使你构建这样的 APT:
|
||||
Context同样可以使你构建这样的API:
|
||||
|
||||
```javascript
|
||||
<Menu>
|
||||
@@ -113,19 +113,19 @@ Context 同样可以使你构建这样的 APT:
|
||||
</Menu>
|
||||
```
|
||||
|
||||
通过在 `Menu` 组件下传相关的信息,每个 `MenuItem` 可以与包含他们的 `Menu` 组件沟通.
|
||||
通过在`Menu`组件内下传相关的信息,每个`MenuItem` 可以与包含他们的`Menu`组件沟通。
|
||||
|
||||
**在你用这个API构建组件以前,考虑一下是否有清晰的替代方案** 我们 喜欢像这样简单的用数组传递items:
|
||||
**在你用这个API构建组件以前,考虑一下是否有清晰的替代方案** 我们喜欢用数组传递items,就像下面这样:
|
||||
|
||||
```javascript
|
||||
<Menu items={['aubergine', 'butternut squash', 'clementine']} />
|
||||
```
|
||||
|
||||
记住你同样可以在props里传递整个React组件,如果你想.
|
||||
记住,如果你愿意,你同样可以在props里传递整个React组件。
|
||||
|
||||
## 在生命周期方法里引用 context
|
||||
## 在生命周期方法里引用context
|
||||
|
||||
如果 `contextTypes` 在一个组件中定义,接下来的生命周期方法会收到一个额外的参数, `context` 对象:
|
||||
如果`contextTypes`是定义在一个组件中,接下来的生命周期方法会收到一个额外的参数,`context`对象:
|
||||
|
||||
```javascript
|
||||
void componentWillReceiveProps(
|
||||
@@ -147,7 +147,7 @@ void componentDidUpdate(
|
||||
|
||||
## 在无状态函数组件里引用 context
|
||||
|
||||
无状态函数同样能够引用 `context` 如果 `contextTypes` 被定义为函数的属性.下面的代码展示了被写为无状态函数组件的 `Button` 组件.
|
||||
如果 `contextTypes` 被定义为函数的属性,无状态函数同样能够引用`context`。下面的代码展示了被写为无状态函数组件的`Button`组件:
|
||||
|
||||
```javascript
|
||||
function Button(props, context) {
|
||||
@@ -160,9 +160,9 @@ function Button(props, context) {
|
||||
Button.contextTypes = {color: React.PropTypes.string};
|
||||
```
|
||||
|
||||
## Updating context
|
||||
## 更新context
|
||||
|
||||
当 state 或者 props 变化时 `getChildContext` 函数会被调用。为了更新context里的数据,用 `this.setState` 触发一个本地的state更新。这将会触发一个新的 context 并且子级将收到变化。
|
||||
当state或props改变时,会调用`getChildContext`方法。为了能更新context内的数据,需要使用`this.setState`来触发组件state更新。这将会触发一个新的context,并且子组件也能接收变化。
|
||||
|
||||
```javascript
|
||||
var MediaQuery = React.createClass({
|
||||
@@ -179,10 +179,10 @@ var MediaQuery = React.createClass({
|
||||
var checkMediaQuery = function(){
|
||||
var type = window.matchMedia("(min-width: 1025px)").matches ? 'desktop' : 'mobile';
|
||||
if (type !== this.state.type){
|
||||
this.setState({type:type});
|
||||
this.setState({type:type});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
window.addEventListener('resize', checkMediaQuery);
|
||||
checkMediaQuery();
|
||||
},
|
||||
@@ -194,12 +194,12 @@ var MediaQuery = React.createClass({
|
||||
|
||||
## 什么时候不用 context
|
||||
|
||||
正像全局变量是在写清晰代码时最好要避免的,你应该在大多数情况下避免使用context. 特别是,在用它来"节省输入"和代替显示传入props时要三思.
|
||||
正如在写清晰代码时最好要避免使用全局变量一样,在大多数情况下,你应该避免使用context。特别是,在用它来"节省输入"和代替显式传入props时要三思。
|
||||
|
||||
context最好的使用场景是隐式的传入登录的用户,当前的语言,或者主题信息.要不然所有这些可能就是全局变量,但是context让你限定他们到一个单独的React树里.
|
||||
隐式传入登录的用户,当前的语言,或者主题信息,是context最好的使用场景。要不然所有这些可能就是全局变量,但是context让你限定他们到一个单独的React树里。
|
||||
|
||||
不要用context在组件里传递你的模型数据.通过树显示的传递你的数据更容易理解.使用context使你的组件更耦合和不可复用,因为 依赖于他们在哪里渲染,他们会表现不同的行为.
|
||||
在组件里传递你的模型数据时,不要依赖context。在组件树内显式传递数据,会更容易令人理解。之所以使用context会增加组件耦合度以及降低复用性,是因为组件在不同的地方渲染时,他们会表现出不同的行为。
|
||||
|
||||
## 已知的限制
|
||||
|
||||
如果一个由组件提供的context值变动,使用那个值的子级不会更新,如果一个直接的父级从 `shouldComponentUpdate` 返回 `false` .详见 issue [#2517](https://github.com/facebook/react/issues/2517) .
|
||||
假设由父组件提供的context值发生变动,但中间父级组件的`shouldComponentUpdate`返回了`false`,那么后代子级不会更新context。详见 issue [#2517](https://github.com/facebook/react/issues/2517) 。
|
||||
|
||||
@@ -7,28 +7,38 @@ next: videos.html
|
||||
---
|
||||
|
||||
### React.js Conf 2015
|
||||
January 28 & 29
|
||||
January 28 & 29 in Facebook HQ, CA
|
||||
|
||||
[Website](http://conf2015.reactjs.org/) - [Schedule](http://conf2015.reactjs.org/schedule.html) - [Videos](https://www.youtube.com/playlist?list=PLb0IAmt7-GS1cbw4qonlQztYV1TAW0sCr)
|
||||
|
||||
<iframe width="650" height="315" src="//www.youtube-nocookie.com/embed/KVZ-P-ZI6W4?list=PLb0IAmt7-GS1cbw4qonlQztYV1TAW0sCr" frameborder="0" allowfullscreen></iframe>
|
||||
|
||||
### ReactEurope 2015
|
||||
July 2 & 3
|
||||
July 2 & 3 in Paris, France
|
||||
|
||||
[Website](http://www.react-europe.org/) - [Schedule](http://www.react-europe.org/#schedule)
|
||||
|
||||
### Reactive 2015
|
||||
November 2-4
|
||||
November 2-4 in Bratislava, Slovakia
|
||||
|
||||
[Website](https://reactive2015.com/) - [Schedule](https://reactive2015.com/schedule_speakers.html#schedule)
|
||||
|
||||
### React.js Conf 2016
|
||||
February 22 & 23
|
||||
February 22 & 23 in San Francisco, CA
|
||||
|
||||
[Website](http://conf.reactjs.com/) - [Schedule](http://conf.reactjs.com/schedule.html) - [Videos](https://www.youtube.com/playlist?list=PLb0IAmt7-GS0M8Q95RIc2lOM6nc77q1IY)
|
||||
|
||||
### ReactEurope 2016
|
||||
June 2 & 3
|
||||
June 2 & 3 in Paris, France
|
||||
|
||||
[Website](http://www.react-europe.org/) - [Schedule](http://www.react-europe.org/#schedule)
|
||||
|
||||
### ReactRally 2016
|
||||
August 25-26 in Salt Lake City, UT
|
||||
|
||||
[Website](http://www.reactrally.com/) - [Schedule](http://www.reactrally.com/#/schedule)
|
||||
|
||||
### Reactive 2016
|
||||
October 26-28 in Bratislava, Slovakia
|
||||
|
||||
[Website](https://reactiveconf.com/)
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -98,7 +98,7 @@ Verifies the object is a ReactElement.
|
||||
array React.Children.map(object children, function fn [, object thisArg])
|
||||
```
|
||||
|
||||
Invoke `fn` on every immediate child contained within `children` with `this` set to `thisArg`. If `children` is a nested object or array it will be traversed: `fn` will never be passed the container objects. If children is `null` or `undefined` returns `null` or `undefined` rather than an array.
|
||||
Invoke `fn` on every immediate child contained within `children` with `this` set to `thisArg`. If `children` is a [keyed fragment](/react/docs/create-fragment.html) or array it will be traversed: `fn` will never be passed the container objects. If children is `null` or `undefined` returns `null` or `undefined` rather than an array.
|
||||
|
||||
#### React.Children.forEach
|
||||
|
||||
@@ -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`例子。
|
||||
|
||||
@@ -137,6 +137,7 @@ This is why you shouldn't construct your own instance. Instead, `ReactElement` i
|
||||
|
||||
The `render` method of a `ReactComponent` is expected to return another `ReactElement`. This allows these components to be composed. Ultimately the render resolves into `ReactElement` with a `string` tag which instantiates a DOM `Element` instance and inserts it into the document.
|
||||
|
||||
React 0.14 introduced [stateless functional components](/react/blog/2015/10/07/react-v0.14.html#stateless-functional-components) as an alternative way of defining components. Instead of being a class, it is a simple function that accepts props and is expected to return a `ReactElement`.
|
||||
|
||||
## Formal Type Definitions
|
||||
|
||||
@@ -165,7 +166,7 @@ type ReactDOMElement = {
|
||||
};
|
||||
|
||||
type ReactComponentElement<TProps> = {
|
||||
type : ReactClass<TProps>,
|
||||
type : ReactClass<TProps> | ReactFunctionalComponent<TProps>,
|
||||
props : TProps,
|
||||
key : string | boolean | number | null,
|
||||
ref : string | null
|
||||
@@ -189,5 +190,7 @@ type ReactComponent<TProps> = {
|
||||
props : TProps,
|
||||
render : () => ReactElement
|
||||
};
|
||||
|
||||
type ReactFunctionalComponent<TProps> = (TProps) => ReactElement;
|
||||
```
|
||||
|
||||
|
||||
@@ -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`.
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ It'll also have a few neat features:
|
||||
|
||||
In order to start this tutorial, we're going to require a running server. This will serve purely as an API endpoint which we'll use for getting and saving data. In order to make this as easy as possible, we've created a simple server in a number of scripting languages that does exactly what we need it to do. **You can [view the source](https://github.com/reactjs/react-tutorial/) or [download a zip file](https://github.com/reactjs/react-tutorial/archive/master.zip) containing everything needed to get started.**
|
||||
|
||||
For sake of simplicity, the server we will run uses a `JSON` file as a database. You would not run this in production but it makes it easy to simulate what you might do when consuming an API. Once you start the server, it will support our API endpoint and it will also serve the static pages we need.
|
||||
For sake of simplicity, the server we will run uses a `JSON` file as a database. You would not run this in production but it makes it easy to simulate what you might do when consuming an API. Once you [start the server](https://github.com/reactjs/react-tutorial/#to-use), it will support our API endpoint and it will also serve the static pages we need.
|
||||
|
||||
### Getting started
|
||||
|
||||
@@ -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"}
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
id: tutorial-zh-CN
|
||||
title: 教程
|
||||
permalink: tutorial-zh-CN.html
|
||||
prev: getting-started-zh-CN.html
|
||||
next: thinking-in-react-zh-CN.html
|
||||
---
|
||||
|
||||
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.
BIN
docs/downloads/react-15.0.2.zip
Normal file
BIN
docs/downloads/react-15.0.2.zip
Normal file
Binary file not shown.
@@ -17,7 +17,7 @@ id: home
|
||||
<div class="marketing-col">
|
||||
<h3>Virtual DOM</h3>
|
||||
<p>
|
||||
React abstracts away the DOM from you, giving a simpler programming model and better performance. React can also render on the server using Node, and it can power native apps using <a href="https://facebook.github.io/react-native/">React Native</a>.
|
||||
React abstracts away the DOM, giving a simpler programming model and better performance. React can also render on the server using Node, and it can power native apps using <a href="https://facebook.github.io/react-native/">React Native</a>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="marketing-col">
|
||||
|
||||
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.2
|
||||
*
|
||||
* 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
|
||||
|
||||
8729
docs/js/react.js
vendored
8729
docs/js/react.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -6,11 +6,11 @@
|
||||
"dependencies": {
|
||||
"babel-preset-es2015": "^6.6.0",
|
||||
"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",
|
||||
"watchify": "^3.4.0"
|
||||
"babelify": "^7.3.0",
|
||||
"browserify": "^13.0.0",
|
||||
"react": "^15.0.2",
|
||||
"react-dom": "^15.0.2",
|
||||
"watchify": "^3.7.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "browserify ./index.js -t babelify -o bundle.js",
|
||||
|
||||
@@ -10,7 +10,7 @@ module.exports = {
|
||||
archive: './build/react-' + version + '.zip',
|
||||
},
|
||||
files: [
|
||||
{cwd: './build/starter', src: ['**'], dest: 'react-' + version + '/'},
|
||||
{cwd: './build/starter', src: ['**'], dot: true, dest: 'react-' + version + '/'},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -77,7 +77,7 @@ function writeTempConfig(callback) {
|
||||
function run(done, configPath) {
|
||||
grunt.log.writeln('running jest (this may take a while)');
|
||||
|
||||
var args = ['--harmony', path.join('node_modules', 'jest-cli', 'bin', 'jest')];
|
||||
var args = ['--harmony', path.join('node_modules', 'jest-cli', 'bin', 'jest'), '--runInBand'];
|
||||
if (configPath) {
|
||||
args.push('--config', configPath);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ var addons = {
|
||||
docs: 'two-way-binding-helpers',
|
||||
},
|
||||
Perf: {
|
||||
module: 'ReactDefaultPerf',
|
||||
module: 'ReactPerf',
|
||||
name: 'perf',
|
||||
docs: 'perf',
|
||||
},
|
||||
|
||||
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),
|
||||
|
||||
33
gulpfile.js
33
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,31 @@ 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',
|
||||
'RCTEventEmitter',
|
||||
'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 }],
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
15184
npm-shrinkwrap.json
generated
Normal file
15184
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.1.0",
|
||||
"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",
|
||||
@@ -47,9 +48,9 @@
|
||||
"gulp-babel": "^6.0.0",
|
||||
"gulp-flatten": "^0.2.0",
|
||||
"gzip-js": "~0.3.2",
|
||||
"jest-cli": "^0.9.0",
|
||||
"jest-cli": "^12.0.2",
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.0.1",
|
||||
"object-assign": "^4.1.0",
|
||||
"platform": "^1.1.0",
|
||||
"run-sequence": "^1.1.4",
|
||||
"through2": "^2.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-addons-template",
|
||||
"version": "15.0.0-rc.2",
|
||||
"version": "15.1.0",
|
||||
"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.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-dom",
|
||||
"version": "15.0.0-rc.2",
|
||||
"version": "15.1.0",
|
||||
"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.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.1.0",
|
||||
"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.1.0"
|
||||
}
|
||||
}
|
||||
@@ -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.1.0",
|
||||
"keywords": [
|
||||
"react"
|
||||
],
|
||||
@@ -25,7 +25,7 @@
|
||||
"dependencies": {
|
||||
"fbjs": "^0.8.0",
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.0.1"
|
||||
"object-assign": "^4.1.0"
|
||||
},
|
||||
"browserify": {
|
||||
"transform": [
|
||||
|
||||
@@ -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.1.0';
|
||||
|
||||
@@ -36,6 +36,8 @@ var shallowCompare = require('shallowCompare');
|
||||
* complex data structures this mixin may have false-negatives for deeper
|
||||
* differences. Only mixin to components which have simple props and state, or
|
||||
* use `forceUpdate()` when you know deep data structures have changed.
|
||||
*
|
||||
* See https://facebook.github.io/react/docs/pure-render-mixin.html
|
||||
*/
|
||||
var ReactComponentWithPureRenderMixin = {
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
|
||||
@@ -31,8 +31,11 @@ var numericPropertyRegex = /^\d+$/;
|
||||
var warnedAboutNumeric = false;
|
||||
|
||||
var ReactFragment = {
|
||||
// Wrap a keyed object in an opaque proxy that warns you if you access any
|
||||
// of its properties.
|
||||
/**
|
||||
* Wrap a keyed object in an opaque proxy that warns you if you access any
|
||||
* of its properties.
|
||||
* See https://facebook.github.io/react/docs/create-fragment.html
|
||||
*/
|
||||
create: function(object) {
|
||||
if (typeof object !== 'object' || !object || Array.isArray(object)) {
|
||||
warning(
|
||||
|
||||
@@ -34,7 +34,7 @@ React.addons = {
|
||||
};
|
||||
|
||||
if (__DEV__) {
|
||||
React.addons.Perf = require('ReactDefaultPerf');
|
||||
React.addons.Perf = require('ReactPerf');
|
||||
React.addons.TestUtils = require('ReactTestUtils');
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ var ReactStateSetters = require('ReactStateSetters');
|
||||
|
||||
/**
|
||||
* A simple mixin around ReactLink.forState().
|
||||
* See https://facebook.github.io/react/docs/two-way-binding-helpers.html
|
||||
*/
|
||||
var LinkedStateMixin = {
|
||||
/**
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
var React = require('React');
|
||||
|
||||
/**
|
||||
* Deprecated: An an easy way to express two-way binding with React.
|
||||
* See https://facebook.github.io/react/docs/two-way-binding-helpers.html
|
||||
*
|
||||
* @param {*} value current value of the link
|
||||
* @param {function} requestChange callback to request a change
|
||||
*/
|
||||
|
||||
@@ -16,6 +16,7 @@ var shallowEqual = require('shallowEqual');
|
||||
/**
|
||||
* Does a shallow comparison for props and state.
|
||||
* See ReactComponentWithPureRenderMixin
|
||||
* See also https://facebook.github.io/react/docs/shallow-compare.html
|
||||
*/
|
||||
function shallowCompare(instance, nextProps, nextState) {
|
||||
return (
|
||||
|
||||
@@ -41,6 +41,11 @@ function createTransitionTimeoutPropValidator(transitionType) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* An easy way to perform CSS transitions and animations when a React component
|
||||
* enters or leaves the DOM.
|
||||
* See https://facebook.github.io/react/docs/animation.html#high-level-api-reactcsstransitiongroup
|
||||
*/
|
||||
var ReactCSSTransitionGroup = React.createClass({
|
||||
displayName: 'ReactCSSTransitionGroup',
|
||||
|
||||
|
||||
@@ -16,6 +16,11 @@ var ReactTransitionChildMapping = require('ReactTransitionChildMapping');
|
||||
|
||||
var emptyFunction = require('emptyFunction');
|
||||
|
||||
/**
|
||||
* A basis for animatins. When children are declaratively added or removed,
|
||||
* special lifecycle hooks are called.
|
||||
* See https://facebook.github.io/react/docs/animation.html#low-level-api-reacttransitiongroup
|
||||
*/
|
||||
var ReactTransitionGroup = React.createClass({
|
||||
displayName: 'ReactTransitionGroup',
|
||||
|
||||
|
||||
@@ -66,6 +66,10 @@ function invariantArrayCase(value, spec, command) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a updated shallow copy of an object without mutating the original.
|
||||
* See https://facebook.github.io/react/docs/update.html for details.
|
||||
*/
|
||||
function update(value, spec) {
|
||||
invariant(
|
||||
typeof spec === 'object',
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
|
||||
var React;
|
||||
var ReactDOM;
|
||||
var ReactDOMServer;
|
||||
|
||||
describe('ReactErrorBoundaries', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
ReactDOM = require('ReactDOM');
|
||||
ReactDOMServer = require('ReactDOMServer');
|
||||
React = require('React');
|
||||
});
|
||||
|
||||
@@ -50,11 +52,46 @@ describe('ReactErrorBoundaries', function() {
|
||||
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var container = document.createElement('div');
|
||||
EventPluginHub.putListener = jest.genMockFn();
|
||||
EventPluginHub.putListener = jest.fn();
|
||||
ReactDOM.render(<Boundary />, container);
|
||||
expect(EventPluginHub.putListener).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('renders an error state (ssr)', function() {
|
||||
class Angry extends React.Component {
|
||||
render() {
|
||||
throw new Error('Please, do not render me.');
|
||||
}
|
||||
}
|
||||
|
||||
class Boundary extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {error: false};
|
||||
}
|
||||
render() {
|
||||
if (!this.state.error) {
|
||||
return (<div><button onClick={this.onClick}>ClickMe</button><Angry /></div>);
|
||||
} else {
|
||||
return (<div>Happy Birthday!</div>);
|
||||
}
|
||||
}
|
||||
onClick() {
|
||||
/* do nothing */
|
||||
}
|
||||
unstable_handleError() {
|
||||
this.setState({error: true});
|
||||
}
|
||||
}
|
||||
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var container = document.createElement('div');
|
||||
EventPluginHub.putListener = jest.fn();
|
||||
container.innerHTML = ReactDOMServer.renderToString(<Boundary />);
|
||||
expect(container.firstChild.innerHTML).toBe('Happy Birthday!');
|
||||
expect(EventPluginHub.putListener).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('will catch exceptions in componentWillUnmount', function() {
|
||||
class ErrorBoundary extends React.Component {
|
||||
constructor() {
|
||||
@@ -120,7 +157,7 @@ describe('ReactErrorBoundaries', function() {
|
||||
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var container = document.createElement('div');
|
||||
EventPluginHub.putListener = jest.genMockFn();
|
||||
EventPluginHub.putListener = jest.fn();
|
||||
ReactDOM.render(<Boundary />, container);
|
||||
expect(EventPluginHub.putListener).toBeCalled();
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
/**
|
||||
* Copyright 2016-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 ReactDebugInstanceMap
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var warning = require('warning');
|
||||
|
||||
function checkValidInstance(internalInstance) {
|
||||
if (!internalInstance) {
|
||||
warning(
|
||||
false,
|
||||
'There is an internal error in the React developer tools integration. ' +
|
||||
'Instead of an internal instance, received %s. ' +
|
||||
'Please report this as a bug in React.',
|
||||
internalInstance
|
||||
);
|
||||
return false;
|
||||
}
|
||||
var isValid = typeof internalInstance.mountComponent === 'function';
|
||||
warning(
|
||||
isValid,
|
||||
'There is an internal error in the React developer tools integration. ' +
|
||||
'Instead of an internal instance, received an object with the following ' +
|
||||
'keys: %s. Please report this as a bug in React.',
|
||||
Object.keys(internalInstance).join(', ')
|
||||
);
|
||||
return isValid;
|
||||
}
|
||||
|
||||
var idCounter = 1;
|
||||
var instancesByIDs = {};
|
||||
var instancesToIDs;
|
||||
|
||||
function getIDForInstance(internalInstance) {
|
||||
if (!instancesToIDs) {
|
||||
instancesToIDs = new WeakMap();
|
||||
}
|
||||
if (instancesToIDs.has(internalInstance)) {
|
||||
return instancesToIDs.get(internalInstance);
|
||||
} else {
|
||||
var instanceID = (idCounter++).toString();
|
||||
instancesToIDs.set(internalInstance, instanceID);
|
||||
return instanceID;
|
||||
}
|
||||
}
|
||||
|
||||
function getInstanceByID(instanceID) {
|
||||
return instancesByIDs[instanceID] || null;
|
||||
}
|
||||
|
||||
function isRegisteredInstance(internalInstance) {
|
||||
var instanceID = getIDForInstance(internalInstance);
|
||||
if (instanceID) {
|
||||
return instancesByIDs.hasOwnProperty(instanceID);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function registerInstance(internalInstance) {
|
||||
var instanceID = getIDForInstance(internalInstance);
|
||||
if (instanceID) {
|
||||
instancesByIDs[instanceID] = internalInstance;
|
||||
}
|
||||
}
|
||||
|
||||
function unregisterInstance(internalInstance) {
|
||||
var instanceID = getIDForInstance(internalInstance);
|
||||
if (instanceID) {
|
||||
delete instancesByIDs[instanceID];
|
||||
}
|
||||
}
|
||||
|
||||
var ReactDebugInstanceMap = {
|
||||
getIDForInstance(internalInstance) {
|
||||
if (!checkValidInstance(internalInstance)) {
|
||||
return null;
|
||||
}
|
||||
return getIDForInstance(internalInstance);
|
||||
},
|
||||
getInstanceByID(instanceID) {
|
||||
return getInstanceByID(instanceID);
|
||||
},
|
||||
isRegisteredInstance(internalInstance) {
|
||||
if (!checkValidInstance(internalInstance)) {
|
||||
return false;
|
||||
}
|
||||
return isRegisteredInstance(internalInstance);
|
||||
},
|
||||
registerInstance(internalInstance) {
|
||||
if (!checkValidInstance(internalInstance)) {
|
||||
return;
|
||||
}
|
||||
warning(
|
||||
!isRegisteredInstance(internalInstance),
|
||||
'There is an internal error in the React developer tools integration. ' +
|
||||
'A registered instance should not be registered again. ' +
|
||||
'Please report this as a bug in React.'
|
||||
);
|
||||
registerInstance(internalInstance);
|
||||
},
|
||||
unregisterInstance(internalInstance) {
|
||||
if (!checkValidInstance(internalInstance)) {
|
||||
return;
|
||||
}
|
||||
warning(
|
||||
isRegisteredInstance(internalInstance),
|
||||
'There is an internal error in the React developer tools integration. ' +
|
||||
'An unregistered instance should not be unregistered again. ' +
|
||||
'Please report this as a bug in React.'
|
||||
);
|
||||
unregisterInstance(internalInstance);
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = ReactDebugInstanceMap;
|
||||
@@ -11,7 +11,9 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var ReactInvalidSetStateWarningDevTool = require('ReactInvalidSetStateWarningDevTool');
|
||||
var ExecutionEnvironment = require('ExecutionEnvironment');
|
||||
|
||||
var performanceNow = require('performanceNow');
|
||||
var warning = require('warning');
|
||||
|
||||
var eventHandlers = [];
|
||||
@@ -37,6 +39,70 @@ function emitEvent(handlerFunctionName, arg1, arg2, arg3, arg4, arg5) {
|
||||
}
|
||||
}
|
||||
|
||||
var isProfiling = false;
|
||||
var flushHistory = [];
|
||||
var currentFlushNesting = 0;
|
||||
var currentFlushMeasurements = null;
|
||||
var currentFlushStartTime = null;
|
||||
var currentTimerDebugID = null;
|
||||
var currentTimerStartTime = null;
|
||||
var currentTimerType = null;
|
||||
|
||||
function clearHistory() {
|
||||
ReactComponentTreeDevtool.purgeUnmountedComponents();
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
}
|
||||
|
||||
function getTreeSnapshot(registeredIDs) {
|
||||
return registeredIDs.reduce((tree, id) => {
|
||||
var ownerID = ReactComponentTreeDevtool.getOwnerID(id);
|
||||
var parentID = ReactComponentTreeDevtool.getParentID(id);
|
||||
tree[id] = {
|
||||
displayName: ReactComponentTreeDevtool.getDisplayName(id),
|
||||
text: ReactComponentTreeDevtool.getText(id),
|
||||
updateCount: ReactComponentTreeDevtool.getUpdateCount(id),
|
||||
childIDs: ReactComponentTreeDevtool.getChildIDs(id),
|
||||
// Text nodes don't have owners but this is close enough.
|
||||
ownerID: ownerID || ReactComponentTreeDevtool.getOwnerID(parentID),
|
||||
parentID,
|
||||
};
|
||||
return tree;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function resetMeasurements() {
|
||||
if (__DEV__) {
|
||||
var previousStartTime = currentFlushStartTime;
|
||||
var previousMeasurements = currentFlushMeasurements || [];
|
||||
var previousOperations = ReactNativeOperationHistoryDevtool.getHistory();
|
||||
|
||||
if (!isProfiling || currentFlushNesting === 0) {
|
||||
currentFlushStartTime = null;
|
||||
currentFlushMeasurements = null;
|
||||
clearHistory();
|
||||
return;
|
||||
}
|
||||
|
||||
if (previousMeasurements.length || previousOperations.length) {
|
||||
var registeredIDs = ReactComponentTreeDevtool.getRegisteredIDs();
|
||||
flushHistory.push({
|
||||
duration: performanceNow() - previousStartTime,
|
||||
measurements: previousMeasurements || [],
|
||||
operations: previousOperations || [],
|
||||
treeSnapshot: getTreeSnapshot(registeredIDs),
|
||||
});
|
||||
}
|
||||
|
||||
clearHistory();
|
||||
currentFlushStartTime = performanceNow();
|
||||
currentFlushMeasurements = [];
|
||||
}
|
||||
}
|
||||
|
||||
function checkDebugID(debugID) {
|
||||
warning(debugID, 'ReactDebugTool: debugID may not be empty.');
|
||||
}
|
||||
|
||||
var ReactDebugTool = {
|
||||
addDevtool(devtool) {
|
||||
eventHandlers.push(devtool);
|
||||
@@ -49,29 +115,157 @@ var ReactDebugTool = {
|
||||
}
|
||||
}
|
||||
},
|
||||
beginProfiling() {
|
||||
if (__DEV__) {
|
||||
if (isProfiling) {
|
||||
return;
|
||||
}
|
||||
|
||||
isProfiling = true;
|
||||
flushHistory.length = 0;
|
||||
resetMeasurements();
|
||||
}
|
||||
},
|
||||
endProfiling() {
|
||||
if (__DEV__) {
|
||||
if (!isProfiling) {
|
||||
return;
|
||||
}
|
||||
|
||||
isProfiling = false;
|
||||
resetMeasurements();
|
||||
}
|
||||
},
|
||||
getFlushHistory() {
|
||||
if (__DEV__) {
|
||||
return flushHistory;
|
||||
}
|
||||
},
|
||||
onBeginFlush() {
|
||||
if (__DEV__) {
|
||||
currentFlushNesting++;
|
||||
resetMeasurements();
|
||||
}
|
||||
emitEvent('onBeginFlush');
|
||||
},
|
||||
onEndFlush() {
|
||||
if (__DEV__) {
|
||||
resetMeasurements();
|
||||
currentFlushNesting--;
|
||||
}
|
||||
emitEvent('onEndFlush');
|
||||
},
|
||||
onBeginLifeCycleTimer(debugID, timerType) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onBeginLifeCycleTimer', debugID, timerType);
|
||||
if (__DEV__) {
|
||||
if (isProfiling && currentFlushNesting > 0) {
|
||||
warning(
|
||||
!currentTimerType,
|
||||
'There is an internal error in the React performance measurement code. ' +
|
||||
'Did not expect %s timer to start while %s timer is still in ' +
|
||||
'progress for %s instance.',
|
||||
timerType,
|
||||
currentTimerType || 'no',
|
||||
(debugID === currentTimerDebugID) ? 'the same' : 'another'
|
||||
);
|
||||
currentTimerStartTime = performanceNow();
|
||||
currentTimerDebugID = debugID;
|
||||
currentTimerType = timerType;
|
||||
}
|
||||
}
|
||||
},
|
||||
onEndLifeCycleTimer(debugID, timerType) {
|
||||
checkDebugID(debugID);
|
||||
if (__DEV__) {
|
||||
if (isProfiling && currentFlushNesting > 0) {
|
||||
warning(
|
||||
currentTimerType === timerType,
|
||||
'There is an internal error in the React performance measurement code. ' +
|
||||
'We did not expect %s timer to stop while %s timer is still in ' +
|
||||
'progress for %s instance. Please report this as a bug in React.',
|
||||
timerType,
|
||||
currentTimerType || 'no',
|
||||
(debugID === currentTimerDebugID) ? 'the same' : 'another'
|
||||
);
|
||||
currentFlushMeasurements.push({
|
||||
timerType,
|
||||
instanceID: debugID,
|
||||
duration: performanceNow() - currentTimerStartTime,
|
||||
});
|
||||
currentTimerStartTime = null;
|
||||
currentTimerDebugID = null;
|
||||
currentTimerType = null;
|
||||
}
|
||||
}
|
||||
emitEvent('onEndLifeCycleTimer', debugID, timerType);
|
||||
},
|
||||
onBeginReconcilerTimer(debugID, timerType) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onBeginReconcilerTimer', debugID, timerType);
|
||||
},
|
||||
onEndReconcilerTimer(debugID, timerType) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onEndReconcilerTimer', debugID, timerType);
|
||||
},
|
||||
onBeginProcessingChildContext() {
|
||||
emitEvent('onBeginProcessingChildContext');
|
||||
},
|
||||
onEndProcessingChildContext() {
|
||||
emitEvent('onEndProcessingChildContext');
|
||||
},
|
||||
onNativeOperation(debugID, type, payload) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onNativeOperation', debugID, type, payload);
|
||||
},
|
||||
onSetState() {
|
||||
emitEvent('onSetState');
|
||||
},
|
||||
onMountRootComponent(internalInstance) {
|
||||
emitEvent('onMountRootComponent', internalInstance);
|
||||
onSetDisplayName(debugID, displayName) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onSetDisplayName', debugID, displayName);
|
||||
},
|
||||
onMountComponent(internalInstance) {
|
||||
emitEvent('onMountComponent', internalInstance);
|
||||
onSetChildren(debugID, childDebugIDs) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onSetChildren', debugID, childDebugIDs);
|
||||
},
|
||||
onUpdateComponent(internalInstance) {
|
||||
emitEvent('onUpdateComponent', internalInstance);
|
||||
onSetOwner(debugID, ownerDebugID) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onSetOwner', debugID, ownerDebugID);
|
||||
},
|
||||
onUnmountComponent(internalInstance) {
|
||||
emitEvent('onUnmountComponent', internalInstance);
|
||||
onSetText(debugID, text) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onSetText', debugID, text);
|
||||
},
|
||||
onMountRootComponent(debugID) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onMountRootComponent', debugID);
|
||||
},
|
||||
onMountComponent(debugID) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onMountComponent', debugID);
|
||||
},
|
||||
onUpdateComponent(debugID) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onUpdateComponent', debugID);
|
||||
},
|
||||
onUnmountComponent(debugID) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onUnmountComponent', debugID);
|
||||
},
|
||||
};
|
||||
|
||||
ReactDebugTool.addDevtool(ReactInvalidSetStateWarningDevTool);
|
||||
if (__DEV__) {
|
||||
var ReactInvalidSetStateWarningDevTool = require('ReactInvalidSetStateWarningDevTool');
|
||||
var ReactNativeOperationHistoryDevtool = require('ReactNativeOperationHistoryDevtool');
|
||||
var ReactComponentTreeDevtool = require('ReactComponentTreeDevtool');
|
||||
ReactDebugTool.addDevtool(ReactInvalidSetStateWarningDevTool);
|
||||
ReactDebugTool.addDevtool(ReactComponentTreeDevtool);
|
||||
ReactDebugTool.addDevtool(ReactNativeOperationHistoryDevtool);
|
||||
var url = (ExecutionEnvironment.canUseDOM && window.location.href) || '';
|
||||
if ((/[?&]react_perf\b/).test(url)) {
|
||||
ReactDebugTool.beginProfiling();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ReactDebugTool;
|
||||
|
||||
371
src/isomorphic/ReactPerf.js
Normal file
371
src/isomorphic/ReactPerf.js
Normal file
@@ -0,0 +1,371 @@
|
||||
/**
|
||||
* Copyright 2016-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 ReactPerf
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var ReactDebugTool = require('ReactDebugTool');
|
||||
var warning = require('warning');
|
||||
|
||||
function roundFloat(val, base = 2) {
|
||||
var n = Math.pow(10, base);
|
||||
return Math.floor(val * n) / n;
|
||||
}
|
||||
|
||||
function getFlushHistory() {
|
||||
return ReactDebugTool.getFlushHistory();
|
||||
}
|
||||
|
||||
function getExclusive(flushHistory = getFlushHistory()) {
|
||||
var aggregatedStats = {};
|
||||
var affectedIDs = {};
|
||||
|
||||
function updateAggregatedStats(treeSnapshot, instanceID, timerType, applyUpdate) {
|
||||
var {displayName} = treeSnapshot[instanceID];
|
||||
var key = displayName;
|
||||
var stats = aggregatedStats[key];
|
||||
if (!stats) {
|
||||
affectedIDs[key] = {};
|
||||
stats = aggregatedStats[key] = {
|
||||
key,
|
||||
instanceCount: 0,
|
||||
counts: {},
|
||||
durations: {},
|
||||
totalDuration: 0,
|
||||
};
|
||||
}
|
||||
if (!stats.durations[timerType]) {
|
||||
stats.durations[timerType] = 0;
|
||||
}
|
||||
if (!stats.counts[timerType]) {
|
||||
stats.counts[timerType] = 0;
|
||||
}
|
||||
affectedIDs[key][instanceID] = true;
|
||||
applyUpdate(stats);
|
||||
}
|
||||
|
||||
flushHistory.forEach(flush => {
|
||||
var {measurements, treeSnapshot} = flush;
|
||||
measurements.forEach(measurement => {
|
||||
var {duration, instanceID, timerType} = measurement;
|
||||
updateAggregatedStats(treeSnapshot, instanceID, timerType, stats => {
|
||||
stats.totalDuration += duration;
|
||||
stats.durations[timerType] += duration;
|
||||
stats.counts[timerType]++;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return Object.keys(aggregatedStats)
|
||||
.map(key => ({
|
||||
...aggregatedStats[key],
|
||||
instanceCount: Object.keys(affectedIDs[key]).length,
|
||||
}))
|
||||
.sort((a, b) =>
|
||||
b.totalDuration - a.totalDuration
|
||||
);
|
||||
}
|
||||
|
||||
function getInclusive(flushHistory = getFlushHistory()) {
|
||||
var aggregatedStats = {};
|
||||
var affectedIDs = {};
|
||||
|
||||
function updateAggregatedStats(treeSnapshot, instanceID, applyUpdate) {
|
||||
var {displayName, ownerID} = treeSnapshot[instanceID];
|
||||
var owner = treeSnapshot[ownerID];
|
||||
var key = (owner ? owner.displayName + ' > ' : '') + displayName;
|
||||
var stats = aggregatedStats[key];
|
||||
if (!stats) {
|
||||
affectedIDs[key] = {};
|
||||
stats = aggregatedStats[key] = {
|
||||
key,
|
||||
instanceCount: 0,
|
||||
inclusiveRenderDuration: 0,
|
||||
renderCount: 0,
|
||||
};
|
||||
}
|
||||
affectedIDs[key][instanceID] = true;
|
||||
applyUpdate(stats);
|
||||
}
|
||||
|
||||
var isCompositeByID = {};
|
||||
flushHistory.forEach(flush => {
|
||||
var {measurements} = flush;
|
||||
measurements.forEach(measurement => {
|
||||
var {instanceID, timerType} = measurement;
|
||||
if (timerType !== 'render') {
|
||||
return;
|
||||
}
|
||||
isCompositeByID[instanceID] = true;
|
||||
});
|
||||
});
|
||||
|
||||
flushHistory.forEach(flush => {
|
||||
var {measurements, treeSnapshot} = flush;
|
||||
measurements.forEach(measurement => {
|
||||
var {duration, instanceID, timerType} = measurement;
|
||||
if (timerType !== 'render') {
|
||||
return;
|
||||
}
|
||||
updateAggregatedStats(treeSnapshot, instanceID, stats => {
|
||||
stats.renderCount++;
|
||||
});
|
||||
var nextParentID = instanceID;
|
||||
while (nextParentID) {
|
||||
// As we traverse parents, only count inclusive time towards composites.
|
||||
// We know something is a composite if its render() was called.
|
||||
if (isCompositeByID[nextParentID]) {
|
||||
updateAggregatedStats(treeSnapshot, nextParentID, stats => {
|
||||
stats.inclusiveRenderDuration += duration;
|
||||
});
|
||||
}
|
||||
nextParentID = treeSnapshot[nextParentID].parentID;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return Object.keys(aggregatedStats)
|
||||
.map(key => ({
|
||||
...aggregatedStats[key],
|
||||
instanceCount: Object.keys(affectedIDs[key]).length,
|
||||
}))
|
||||
.sort((a, b) =>
|
||||
b.inclusiveRenderDuration - a.inclusiveRenderDuration
|
||||
);
|
||||
}
|
||||
|
||||
function getWasted(flushHistory = getFlushHistory()) {
|
||||
var aggregatedStats = {};
|
||||
var affectedIDs = {};
|
||||
|
||||
function updateAggregatedStats(treeSnapshot, instanceID, applyUpdate) {
|
||||
var {displayName, ownerID} = treeSnapshot[instanceID];
|
||||
var owner = treeSnapshot[ownerID];
|
||||
var key = (owner ? owner.displayName + ' > ' : '') + displayName;
|
||||
var stats = aggregatedStats[key];
|
||||
if (!stats) {
|
||||
affectedIDs[key] = {};
|
||||
stats = aggregatedStats[key] = {
|
||||
key,
|
||||
instanceCount: 0,
|
||||
inclusiveRenderDuration: 0,
|
||||
renderCount: 0,
|
||||
};
|
||||
}
|
||||
affectedIDs[key][instanceID] = true;
|
||||
applyUpdate(stats);
|
||||
}
|
||||
|
||||
flushHistory.forEach(flush => {
|
||||
var {measurements, treeSnapshot, operations} = flush;
|
||||
var isDefinitelyNotWastedByID = {};
|
||||
|
||||
// Find native components associated with an operation in this batch.
|
||||
// Mark all components in their parent tree as definitely not wasted.
|
||||
operations.forEach(operation => {
|
||||
var {instanceID} = operation;
|
||||
var nextParentID = instanceID;
|
||||
while (nextParentID) {
|
||||
isDefinitelyNotWastedByID[nextParentID] = true;
|
||||
nextParentID = treeSnapshot[nextParentID].parentID;
|
||||
}
|
||||
});
|
||||
|
||||
// Find composite components that rendered in this batch.
|
||||
// These are potential candidates for being wasted renders.
|
||||
var renderedCompositeIDs = {};
|
||||
measurements.forEach(measurement => {
|
||||
var {instanceID, timerType} = measurement;
|
||||
if (timerType !== 'render') {
|
||||
return;
|
||||
}
|
||||
renderedCompositeIDs[instanceID] = true;
|
||||
});
|
||||
|
||||
measurements.forEach(measurement => {
|
||||
var {duration, instanceID, timerType} = measurement;
|
||||
if (timerType !== 'render') {
|
||||
return;
|
||||
}
|
||||
|
||||
// If there was a DOM update below this component, or it has just been
|
||||
// mounted, its render() is not considered wasted.
|
||||
var { updateCount } = treeSnapshot[instanceID];
|
||||
if (isDefinitelyNotWastedByID[instanceID] || updateCount === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We consider this render() wasted.
|
||||
updateAggregatedStats(treeSnapshot, instanceID, stats => {
|
||||
stats.renderCount++;
|
||||
});
|
||||
|
||||
var nextParentID = instanceID;
|
||||
while (nextParentID) {
|
||||
// Any parents rendered during this batch are considered wasted
|
||||
// unless we previously marked them as dirty.
|
||||
var isWasted =
|
||||
renderedCompositeIDs[nextParentID] &&
|
||||
!isDefinitelyNotWastedByID[nextParentID];
|
||||
if (isWasted) {
|
||||
updateAggregatedStats(treeSnapshot, nextParentID, stats => {
|
||||
stats.inclusiveRenderDuration += duration;
|
||||
});
|
||||
}
|
||||
nextParentID = treeSnapshot[nextParentID].parentID;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return Object.keys(aggregatedStats)
|
||||
.map(key => ({
|
||||
...aggregatedStats[key],
|
||||
instanceCount: Object.keys(affectedIDs[key]).length,
|
||||
}))
|
||||
.sort((a, b) =>
|
||||
b.inclusiveRenderDuration - a.inclusiveRenderDuration
|
||||
);
|
||||
}
|
||||
|
||||
function getOperations(flushHistory = getFlushHistory()) {
|
||||
var stats = [];
|
||||
flushHistory.forEach((flush, flushIndex) => {
|
||||
var {operations, treeSnapshot} = flush;
|
||||
operations.forEach(operation => {
|
||||
var {instanceID, type, payload} = operation;
|
||||
var {displayName, ownerID} = treeSnapshot[instanceID];
|
||||
var owner = treeSnapshot[ownerID];
|
||||
var key = (owner ? owner.displayName + ' > ' : '') + displayName;
|
||||
|
||||
stats.push({
|
||||
flushIndex,
|
||||
instanceID,
|
||||
key,
|
||||
type,
|
||||
ownerID,
|
||||
payload,
|
||||
});
|
||||
});
|
||||
});
|
||||
return stats;
|
||||
}
|
||||
|
||||
function printExclusive(flushHistory) {
|
||||
var stats = getExclusive(flushHistory);
|
||||
var table = stats.map(item => {
|
||||
var {key, instanceCount, totalDuration} = item;
|
||||
var renderCount = item.counts.render || 0;
|
||||
var renderDuration = item.durations.render || 0;
|
||||
return {
|
||||
'Component': key,
|
||||
'Total time (ms)': roundFloat(totalDuration),
|
||||
'Instance count': instanceCount,
|
||||
'Total render time (ms)': roundFloat(renderDuration),
|
||||
'Average render time (ms)': renderCount ?
|
||||
roundFloat(renderDuration / renderCount) :
|
||||
undefined,
|
||||
'Render count': renderCount,
|
||||
'Total lifecycle time (ms)': roundFloat(totalDuration - renderDuration),
|
||||
};
|
||||
});
|
||||
console.table(table);
|
||||
}
|
||||
|
||||
function printInclusive(flushHistory) {
|
||||
var stats = getInclusive(flushHistory);
|
||||
var table = stats.map(item => {
|
||||
var {key, instanceCount, inclusiveRenderDuration, renderCount} = item;
|
||||
return {
|
||||
'Owner > Component': key,
|
||||
'Inclusive render time (ms)': roundFloat(inclusiveRenderDuration),
|
||||
'Instance count': instanceCount,
|
||||
'Render count': renderCount,
|
||||
};
|
||||
});
|
||||
console.table(table);
|
||||
}
|
||||
|
||||
function printWasted(flushHistory) {
|
||||
var stats = getWasted(flushHistory);
|
||||
var table = stats.map(item => {
|
||||
var {key, instanceCount, inclusiveRenderDuration, renderCount} = item;
|
||||
return {
|
||||
'Owner > Component': key,
|
||||
'Inclusive wasted time (ms)': roundFloat(inclusiveRenderDuration),
|
||||
'Instance count': instanceCount,
|
||||
'Render count': renderCount,
|
||||
};
|
||||
});
|
||||
console.table(table);
|
||||
}
|
||||
|
||||
function printOperations(flushHistory) {
|
||||
var stats = getOperations(flushHistory);
|
||||
var table = stats.map(stat => ({
|
||||
'Owner > Node': stat.key,
|
||||
'Operation': stat.type,
|
||||
'Payload': typeof stat.payload === 'object' ?
|
||||
JSON.stringify(stat.payload) :
|
||||
stat.payload,
|
||||
'Flush index': stat.flushIndex,
|
||||
'Owner Component ID': stat.ownerID,
|
||||
'DOM Component ID': stat.instanceID,
|
||||
}));
|
||||
console.table(table);
|
||||
}
|
||||
|
||||
var warnedAboutPrintDOM = false;
|
||||
function printDOM(measurements) {
|
||||
warning(
|
||||
warnedAboutPrintDOM,
|
||||
'`ReactPerf.printDOM(...)` is deprecated. Use ' +
|
||||
'`ReactPerf.printOperations(...)` instead.'
|
||||
);
|
||||
warnedAboutPrintDOM = true;
|
||||
return printOperations(measurements);
|
||||
}
|
||||
|
||||
var warnedAboutGetMeasurementsSummaryMap = false;
|
||||
function getMeasurementsSummaryMap(measurements) {
|
||||
warning(
|
||||
warnedAboutGetMeasurementsSummaryMap,
|
||||
'`ReactPerf.getMeasurementsSummaryMap(...)` is deprecated. Use ' +
|
||||
'`ReactPerf.getWasted(...)` instead.'
|
||||
);
|
||||
warnedAboutGetMeasurementsSummaryMap = true;
|
||||
return getWasted(measurements);
|
||||
}
|
||||
|
||||
function start() {
|
||||
ReactDebugTool.beginProfiling();
|
||||
}
|
||||
|
||||
function stop() {
|
||||
ReactDebugTool.endProfiling();
|
||||
}
|
||||
|
||||
var ReactPerfAnalysis = {
|
||||
getLastMeasurements: getFlushHistory,
|
||||
getExclusive,
|
||||
getInclusive,
|
||||
getWasted,
|
||||
getOperations,
|
||||
printExclusive,
|
||||
printInclusive,
|
||||
printWasted,
|
||||
printOperations,
|
||||
start,
|
||||
stop,
|
||||
// Deprecated:
|
||||
printDOM,
|
||||
getMeasurementsSummaryMap,
|
||||
};
|
||||
|
||||
module.exports = ReactPerfAnalysis;
|
||||
@@ -1,173 +0,0 @@
|
||||
/**
|
||||
* Copyright 2016-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('ReactDebugInstanceMap', function() {
|
||||
var React;
|
||||
var ReactDebugInstanceMap;
|
||||
var ReactDOM;
|
||||
|
||||
beforeEach(function() {
|
||||
jest.resetModuleRegistry();
|
||||
React = require('React');
|
||||
ReactDebugInstanceMap = require('ReactDebugInstanceMap');
|
||||
ReactDOM = require('ReactDOM');
|
||||
});
|
||||
|
||||
function createStubInstance() {
|
||||
return { mountComponent: () => {} };
|
||||
}
|
||||
|
||||
it('should register and unregister instances', function() {
|
||||
var inst1 = createStubInstance();
|
||||
var inst2 = createStubInstance();
|
||||
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(false);
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(false);
|
||||
|
||||
ReactDebugInstanceMap.registerInstance(inst1);
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(true);
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(false);
|
||||
|
||||
ReactDebugInstanceMap.registerInstance(inst2);
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(true);
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(true);
|
||||
|
||||
ReactDebugInstanceMap.unregisterInstance(inst2);
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(true);
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(false);
|
||||
|
||||
ReactDebugInstanceMap.unregisterInstance(inst1);
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(false);
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(false);
|
||||
});
|
||||
|
||||
it('should assign stable IDs', function() {
|
||||
var inst1 = createStubInstance();
|
||||
var inst2 = createStubInstance();
|
||||
|
||||
var inst1ID = ReactDebugInstanceMap.getIDForInstance(inst1);
|
||||
var inst2ID = ReactDebugInstanceMap.getIDForInstance(inst2);
|
||||
expect(typeof inst1ID).toBe('string');
|
||||
expect(typeof inst2ID).toBe('string');
|
||||
expect(inst1ID).not.toBe(inst2ID);
|
||||
|
||||
ReactDebugInstanceMap.registerInstance(inst1);
|
||||
ReactDebugInstanceMap.registerInstance(inst2);
|
||||
expect(ReactDebugInstanceMap.getIDForInstance(inst1)).toBe(inst1ID);
|
||||
expect(ReactDebugInstanceMap.getIDForInstance(inst2)).toBe(inst2ID);
|
||||
|
||||
ReactDebugInstanceMap.unregisterInstance(inst1);
|
||||
ReactDebugInstanceMap.unregisterInstance(inst2);
|
||||
expect(ReactDebugInstanceMap.getIDForInstance(inst1)).toBe(inst1ID);
|
||||
expect(ReactDebugInstanceMap.getIDForInstance(inst2)).toBe(inst2ID);
|
||||
});
|
||||
|
||||
it('should retrieve registered instance by its ID', function() {
|
||||
var inst1 = createStubInstance();
|
||||
var inst2 = createStubInstance();
|
||||
|
||||
var inst1ID = ReactDebugInstanceMap.getIDForInstance(inst1);
|
||||
var inst2ID = ReactDebugInstanceMap.getIDForInstance(inst2);
|
||||
expect(ReactDebugInstanceMap.getInstanceByID(inst1ID)).toBe(null);
|
||||
expect(ReactDebugInstanceMap.getInstanceByID(inst2ID)).toBe(null);
|
||||
|
||||
ReactDebugInstanceMap.registerInstance(inst1);
|
||||
ReactDebugInstanceMap.registerInstance(inst2);
|
||||
expect(ReactDebugInstanceMap.getInstanceByID(inst1ID)).toBe(inst1);
|
||||
expect(ReactDebugInstanceMap.getInstanceByID(inst2ID)).toBe(inst2);
|
||||
|
||||
ReactDebugInstanceMap.unregisterInstance(inst1);
|
||||
ReactDebugInstanceMap.unregisterInstance(inst2);
|
||||
expect(ReactDebugInstanceMap.getInstanceByID(inst1ID)).toBe(null);
|
||||
expect(ReactDebugInstanceMap.getInstanceByID(inst2ID)).toBe(null);
|
||||
});
|
||||
|
||||
it('should warn when registering an instance twice', function() {
|
||||
spyOn(console, 'error');
|
||||
|
||||
var inst = createStubInstance();
|
||||
ReactDebugInstanceMap.registerInstance(inst);
|
||||
expect(console.error.argsForCall.length).toBe(0);
|
||||
|
||||
ReactDebugInstanceMap.registerInstance(inst);
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'There is an internal error in the React developer tools integration. ' +
|
||||
'A registered instance should not be registered again. ' +
|
||||
'Please report this as a bug in React.'
|
||||
);
|
||||
|
||||
ReactDebugInstanceMap.unregisterInstance(inst);
|
||||
ReactDebugInstanceMap.registerInstance(inst);
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should warn when unregistering an instance twice', function() {
|
||||
spyOn(console, 'error');
|
||||
var inst = createStubInstance();
|
||||
|
||||
ReactDebugInstanceMap.unregisterInstance(inst);
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'There is an internal error in the React developer tools integration. ' +
|
||||
'An unregistered instance should not be unregistered again. ' +
|
||||
'Please report this as a bug in React.'
|
||||
);
|
||||
|
||||
ReactDebugInstanceMap.registerInstance(inst);
|
||||
ReactDebugInstanceMap.unregisterInstance(inst);
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
|
||||
ReactDebugInstanceMap.unregisterInstance(inst);
|
||||
expect(console.error.argsForCall.length).toBe(2);
|
||||
expect(console.error.argsForCall[1][0]).toContain(
|
||||
'There is an internal error in the React developer tools integration. ' +
|
||||
'An unregistered instance should not be unregistered again. ' +
|
||||
'Please report this as a bug in React.'
|
||||
);
|
||||
});
|
||||
|
||||
it('should warn about anything than is not an internal instance', function() {
|
||||
class Foo extends React.Component {
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
spyOn(console, 'error');
|
||||
var warningCount = 0;
|
||||
var div = document.createElement('div');
|
||||
var publicInst = ReactDOM.render(<Foo />, div);
|
||||
|
||||
[false, null, undefined, {}, div, publicInst].forEach(falsyValue => {
|
||||
ReactDebugInstanceMap.registerInstance(falsyValue);
|
||||
warningCount++;
|
||||
expect(ReactDebugInstanceMap.getIDForInstance(falsyValue)).toBe(null);
|
||||
warningCount++;
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(falsyValue)).toBe(false);
|
||||
warningCount++;
|
||||
ReactDebugInstanceMap.unregisterInstance(falsyValue);
|
||||
warningCount++;
|
||||
});
|
||||
|
||||
expect(console.error.argsForCall.length).toBe(warningCount);
|
||||
for (var i = 0; i < warningCount.length; i++) {
|
||||
// Ideally we could check for the more detailed error message here
|
||||
// but it depends on the input type and is meant for internal bugs
|
||||
// anyway so I don't think it's worth complicating the test with it.
|
||||
expect(console.error.argsForCall[i][0]).toContain(
|
||||
'There is an internal error in the React developer tools integration.'
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -11,13 +11,11 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
describe('ReactDefaultPerf', function() {
|
||||
describe('ReactPerf', function() {
|
||||
var React;
|
||||
var ReactDOM;
|
||||
var ReactDOMFeatureFlags;
|
||||
var ReactDefaultPerf;
|
||||
var ReactPerf;
|
||||
var ReactTestUtils;
|
||||
var ReactDefaultPerfAnalysis;
|
||||
|
||||
var App;
|
||||
var Box;
|
||||
@@ -36,10 +34,8 @@ describe('ReactDefaultPerf', function() {
|
||||
|
||||
React = require('React');
|
||||
ReactDOM = require('ReactDOM');
|
||||
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
|
||||
ReactDefaultPerf = require('ReactDefaultPerf');
|
||||
ReactPerf = require('ReactPerf');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
ReactDefaultPerfAnalysis = require('ReactDefaultPerfAnalysis');
|
||||
|
||||
App = React.createClass({
|
||||
render: function() {
|
||||
@@ -68,10 +64,17 @@ describe('ReactDefaultPerf', function() {
|
||||
});
|
||||
|
||||
function measure(fn) {
|
||||
ReactDefaultPerf.start();
|
||||
ReactPerf.start();
|
||||
fn();
|
||||
ReactDefaultPerf.stop();
|
||||
return ReactDefaultPerf.getLastMeasurements().__unstable_this_format_will_change;
|
||||
ReactPerf.stop();
|
||||
|
||||
// Make sure none of the methods crash.
|
||||
ReactPerf.getWasted();
|
||||
ReactPerf.getInclusive();
|
||||
ReactPerf.getExclusive();
|
||||
ReactPerf.getOperations();
|
||||
|
||||
return ReactPerf.getLastMeasurements();
|
||||
}
|
||||
|
||||
it('should count no-op update as waste', function() {
|
||||
@@ -81,20 +84,18 @@ describe('ReactDefaultPerf', function() {
|
||||
ReactDOM.render(<App />, container);
|
||||
});
|
||||
|
||||
var summary = ReactDefaultPerf.getWasted(measurements);
|
||||
expect(summary.length).toBe(2);
|
||||
|
||||
/*eslint-disable dot-notation */
|
||||
|
||||
expect(summary[0]['Owner > component']).toBe('<root> > App');
|
||||
expect(summary[0]['Wasted time (ms)']).not.toBe(0);
|
||||
expect(summary[0]['Instances']).toBe(1);
|
||||
|
||||
expect(summary[1]['Owner > component']).toBe('App > Box');
|
||||
expect(summary[1]['Wasted time (ms)']).not.toBe(0);
|
||||
expect(summary[1]['Instances']).toBe(2);
|
||||
|
||||
/*eslint-enable dot-notation */
|
||||
var summary = ReactPerf.getWasted(measurements);
|
||||
expect(summary).toEqual([{
|
||||
key: 'App',
|
||||
instanceCount: 1,
|
||||
inclusiveRenderDuration: 3,
|
||||
renderCount: 1,
|
||||
}, {
|
||||
key: 'App > Box',
|
||||
instanceCount: 2,
|
||||
inclusiveRenderDuration: 2,
|
||||
renderCount: 2,
|
||||
}]);
|
||||
});
|
||||
|
||||
it('should count no-op update in child as waste', function() {
|
||||
@@ -107,21 +108,18 @@ describe('ReactDefaultPerf', function() {
|
||||
ReactDOM.render(<App flipSecond={true} />, container);
|
||||
});
|
||||
|
||||
var summary = ReactDefaultPerf.getWasted(measurements);
|
||||
expect(summary.length).toBe(1);
|
||||
|
||||
/*eslint-disable dot-notation */
|
||||
|
||||
expect(summary[0]['Owner > component']).toBe('App > Box');
|
||||
expect(summary[0]['Wasted time (ms)']).not.toBe(0);
|
||||
expect(summary[0]['Instances']).toBe(1);
|
||||
|
||||
/*eslint-enable dot-notation */
|
||||
var summary = ReactPerf.getWasted(measurements);
|
||||
expect(summary).toEqual([{
|
||||
key: 'App > Box',
|
||||
instanceCount: 1,
|
||||
inclusiveRenderDuration: 1,
|
||||
renderCount: 1,
|
||||
}]);
|
||||
});
|
||||
|
||||
function expectNoWaste(fn) {
|
||||
var measurements = measure(fn);
|
||||
var summary = ReactDefaultPerf.getWasted(measurements);
|
||||
var summary = ReactPerf.getWasted(measurements);
|
||||
expect(summary).toEqual([]);
|
||||
}
|
||||
|
||||
@@ -217,71 +215,72 @@ describe('ReactDefaultPerf', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('putListener should not be instrumented', function() {
|
||||
it('should not count replacing null with a native as waste', function() {
|
||||
var element = null;
|
||||
function Foo() {
|
||||
return element;
|
||||
}
|
||||
var container = document.createElement('div');
|
||||
ReactDOM.render(<Div onClick={function() {}}>hey</Div>, container);
|
||||
var measurements = measure(() => {
|
||||
ReactDOM.render(<Div onClick={function() {}}>hey</Div>, container);
|
||||
ReactDOM.render(<Foo />, container);
|
||||
expectNoWaste(() => {
|
||||
element = <div />;
|
||||
ReactDOM.render(<Foo />, container);
|
||||
});
|
||||
|
||||
var summary = ReactDefaultPerfAnalysis.getDOMSummary(measurements);
|
||||
expect(summary).toEqual([]);
|
||||
});
|
||||
|
||||
it('deleteListener should not be instrumented', function() {
|
||||
it('should not count replacing a native with null as waste', function() {
|
||||
var element = <div />;
|
||||
function Foo() {
|
||||
return element;
|
||||
}
|
||||
var container = document.createElement('div');
|
||||
ReactDOM.render(<Div onClick={function() {}}>hey</Div>, container);
|
||||
var measurements = measure(() => {
|
||||
ReactDOM.render(<Div>hey</Div>, container);
|
||||
ReactDOM.render(<Foo />, container);
|
||||
expectNoWaste(() => {
|
||||
element = null;
|
||||
ReactDOM.render(<Foo />, container);
|
||||
});
|
||||
|
||||
var summary = ReactDefaultPerfAnalysis.getDOMSummary(measurements);
|
||||
expect(summary).toEqual([]);
|
||||
});
|
||||
|
||||
it('should print a table after calling printOperations', function() {
|
||||
it('should include stats for components unmounted during measurement', function() {
|
||||
var container = document.createElement('div');
|
||||
var measurements = measure(() => {
|
||||
ReactDOM.render(<Div>hey</Div>, container);
|
||||
ReactDOM.render(<Div><Div key="a" /></Div>, container);
|
||||
ReactDOM.render(<Div><Div key="b" /></Div>, container);
|
||||
});
|
||||
spyOn(console, 'table');
|
||||
ReactDefaultPerf.printOperations(measurements);
|
||||
expect(console.table.calls.length).toBe(1);
|
||||
expect(console.table.argsForCall[0][0]).toEqual([{
|
||||
'data-reactid': '',
|
||||
type: 'set innerHTML',
|
||||
args: ReactDOMFeatureFlags.useCreateElement ?
|
||||
'{"node":"<not serializable>","children":[],"html":null,"text":null}' :
|
||||
'"<div data-reactroot=\\"\\" data-reactid=\\"1\\">hey</div>"',
|
||||
expect(ReactPerf.getExclusive(measurements)).toEqual([{
|
||||
key: 'Div',
|
||||
instanceCount: 3,
|
||||
counts: { ctor: 3, render: 4 },
|
||||
durations: { ctor: 3, render: 4 },
|
||||
totalDuration: 7,
|
||||
}]);
|
||||
});
|
||||
|
||||
it('warns once when using getMeasurementsSummaryMap', function() {
|
||||
var measurements = measure(() => {});
|
||||
spyOn(console, 'error');
|
||||
ReactDefaultPerf.getMeasurementsSummaryMap(measurements);
|
||||
ReactPerf.getMeasurementsSummaryMap(measurements);
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'`ReactPerf.getMeasurementsSummaryMap(...)` is deprecated. Use ' +
|
||||
'`ReactPerf.getWasted(...)` instead.'
|
||||
);
|
||||
|
||||
ReactDefaultPerf.getMeasurementsSummaryMap(measurements);
|
||||
ReactPerf.getMeasurementsSummaryMap(measurements);
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('warns once when using printDOM', function() {
|
||||
var measurements = measure(() => {});
|
||||
spyOn(console, 'error');
|
||||
ReactDefaultPerf.printDOM(measurements);
|
||||
ReactPerf.printDOM(measurements);
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'`ReactPerf.printDOM(...)` is deprecated. Use ' +
|
||||
'`ReactPerf.printOperations(...)` instead.'
|
||||
);
|
||||
|
||||
ReactDefaultPerf.printDOM(measurements);
|
||||
ReactPerf.printDOM(measurements);
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -55,6 +55,8 @@ function forEachSingleChild(bookKeeping, child, name) {
|
||||
/**
|
||||
* Iterates through children that are typically specified as `props.children`.
|
||||
*
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.children.foreach
|
||||
*
|
||||
* The provided forEachFunc(child, index) will be called for each
|
||||
* leaf child.
|
||||
*
|
||||
@@ -146,6 +148,8 @@ function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
|
||||
/**
|
||||
* Maps children that are typically specified as `props.children`.
|
||||
*
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.children.map
|
||||
*
|
||||
* The provided mapFunction(child, key, index) will be called for each
|
||||
* leaf child.
|
||||
*
|
||||
@@ -173,6 +177,8 @@ function forEachSingleChildDummy(traverseContext, child, name) {
|
||||
* Count the number of children that are typically specified as
|
||||
* `props.children`.
|
||||
*
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.children.count
|
||||
*
|
||||
* @param {?*} children Children tree container.
|
||||
* @return {number} The number of children.
|
||||
*/
|
||||
@@ -184,6 +190,8 @@ function countChildren(children, context) {
|
||||
/**
|
||||
* Flatten a children object (typically specified as `props.children`) and
|
||||
* return an array with appropriately re-keyed children.
|
||||
*
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.children.toarray
|
||||
*/
|
||||
function toArray(children) {
|
||||
var result = [];
|
||||
|
||||
@@ -16,13 +16,16 @@ var invariant = require('invariant');
|
||||
|
||||
/**
|
||||
* Returns the first child in a collection of children and verifies that there
|
||||
* is only one child in the collection. The current implementation of this
|
||||
* function assumes that a single child gets passed without a wrapper, but the
|
||||
* purpose of this helper function is to abstract away the particular structure
|
||||
* of children.
|
||||
* is only one child in the collection.
|
||||
*
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.children.only
|
||||
*
|
||||
* The current implementation of this function assumes that a single child gets
|
||||
* passed without a wrapper, but the purpose of this helper function is to
|
||||
* abstract away the particular structure 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) {
|
||||
|
||||
@@ -740,6 +740,7 @@ var ReactClass = {
|
||||
|
||||
/**
|
||||
* Creates a composite component class given a class specification.
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.createclass
|
||||
*
|
||||
* @param {object} spec Class specification (which must define `render`).
|
||||
* @return {function} Component constructor function.
|
||||
|
||||
@@ -20,9 +20,9 @@ describe('autobinding', function() {
|
||||
|
||||
it('Holds reference to instance', function() {
|
||||
|
||||
var mouseDidEnter = jest.genMockFn();
|
||||
var mouseDidLeave = jest.genMockFn();
|
||||
var mouseDidClick = jest.genMockFn();
|
||||
var mouseDidEnter = jest.fn();
|
||||
var mouseDidLeave = jest.fn();
|
||||
var mouseDidClick = jest.fn();
|
||||
|
||||
var TestBindComponent = React.createClass({
|
||||
getInitialState: function() {
|
||||
@@ -95,7 +95,7 @@ describe('autobinding', function() {
|
||||
});
|
||||
|
||||
it('works with mixins', function() {
|
||||
var mouseDidClick = jest.genMockFn();
|
||||
var mouseDidClick = jest.fn();
|
||||
|
||||
var TestMixin = {
|
||||
onClick: mouseDidClick,
|
||||
|
||||
@@ -20,9 +20,9 @@ describe('autobind optout', function() {
|
||||
|
||||
it('should work with manual binding', function() {
|
||||
|
||||
var mouseDidEnter = jest.genMockFn();
|
||||
var mouseDidLeave = jest.genMockFn();
|
||||
var mouseDidClick = jest.genMockFn();
|
||||
var mouseDidEnter = jest.fn();
|
||||
var mouseDidLeave = jest.fn();
|
||||
var mouseDidClick = jest.fn();
|
||||
|
||||
var TestBindComponent = React.createClass({
|
||||
autobind: false,
|
||||
@@ -138,7 +138,7 @@ describe('autobind optout', function() {
|
||||
});
|
||||
|
||||
it('works with mixins that have not opted out of autobinding', function() {
|
||||
var mouseDidClick = jest.genMockFn();
|
||||
var mouseDidClick = jest.fn();
|
||||
|
||||
var TestMixin = {
|
||||
onClick: mouseDidClick,
|
||||
@@ -164,7 +164,7 @@ describe('autobind optout', function() {
|
||||
});
|
||||
|
||||
it('works with mixins that have opted out of autobinding', function() {
|
||||
var mouseDidClick = jest.genMockFn();
|
||||
var mouseDidClick = jest.fn();
|
||||
|
||||
var TestMixin = {
|
||||
autobind: false,
|
||||
|
||||
@@ -43,7 +43,7 @@ describe('ReactClass-spec', function() {
|
||||
});
|
||||
|
||||
it('should copy prop types onto the Constructor', function() {
|
||||
var propValidator = jest.genMockFn();
|
||||
var propValidator = jest.fn();
|
||||
var TestComponent = React.createClass({
|
||||
propTypes: {
|
||||
value: propValidator,
|
||||
|
||||
@@ -25,8 +25,8 @@ describe('ReactClass-mixin', function() {
|
||||
beforeEach(function() {
|
||||
React = require('React');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
mixinPropValidator = jest.genMockFn();
|
||||
componentPropValidator = jest.genMockFn();
|
||||
mixinPropValidator = jest.fn();
|
||||
componentPropValidator = jest.fn();
|
||||
|
||||
var MixinA = {
|
||||
propTypes: {
|
||||
@@ -107,7 +107,7 @@ describe('ReactClass-mixin', function() {
|
||||
});
|
||||
|
||||
it('should support merging propTypes and statics', function() {
|
||||
var listener = jest.genMockFn();
|
||||
var listener = jest.fn();
|
||||
var instance = <TestComponent listener={listener} />;
|
||||
instance = ReactTestUtils.renderIntoDocument(instance);
|
||||
|
||||
@@ -122,7 +122,7 @@ describe('ReactClass-mixin', function() {
|
||||
});
|
||||
|
||||
it('should support chaining delegate functions', function() {
|
||||
var listener = jest.genMockFn();
|
||||
var listener = jest.fn();
|
||||
var instance = <TestComponent listener={listener} />;
|
||||
instance = ReactTestUtils.renderIntoDocument(instance);
|
||||
|
||||
@@ -135,7 +135,7 @@ describe('ReactClass-mixin', function() {
|
||||
});
|
||||
|
||||
it('should chain functions regardless of spec property order', function() {
|
||||
var listener = jest.genMockFn();
|
||||
var listener = jest.fn();
|
||||
var instance = <TestComponentWithReverseSpec listener={listener} />;
|
||||
instance = ReactTestUtils.renderIntoDocument(instance);
|
||||
|
||||
|
||||
@@ -113,6 +113,10 @@ var ReactElement = function(type, key, ref, self, source, owner, props) {
|
||||
return element;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create and return a new ReactElement of the given type.
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.createelement
|
||||
*/
|
||||
ReactElement.createElement = function(type, config, children) {
|
||||
var propName;
|
||||
|
||||
@@ -126,6 +130,13 @@ ReactElement.createElement = function(type, config, children) {
|
||||
|
||||
if (config != null) {
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
/* eslint-disable no-proto */
|
||||
config.__proto__ == null || config.__proto__ === Object.prototype,
|
||||
/* eslint-enable no-proto */
|
||||
'React.createElement(...): Expected props argument to be a plain object. ' +
|
||||
'Properties defined in its prototype chain will be ignored.'
|
||||
);
|
||||
ref = !config.hasOwnProperty('ref') ||
|
||||
Object.getOwnPropertyDescriptor(config, 'ref').get ? null : config.ref;
|
||||
key = !config.hasOwnProperty('key') ||
|
||||
@@ -223,6 +234,10 @@ ReactElement.createElement = function(type, config, children) {
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a function that produces ReactElements of a given type.
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.createfactory
|
||||
*/
|
||||
ReactElement.createFactory = function(type) {
|
||||
var factory = ReactElement.createElement.bind(null, type);
|
||||
// Expose the type on the factory and the prototype so that it can be
|
||||
@@ -248,6 +263,10 @@ ReactElement.cloneAndReplaceKey = function(oldElement, newKey) {
|
||||
return newElement;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clone and return a new ReactElement using element as the starting point.
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.cloneelement
|
||||
*/
|
||||
ReactElement.cloneElement = function(element, config, children) {
|
||||
var propName;
|
||||
|
||||
@@ -268,6 +287,15 @@ ReactElement.cloneElement = function(element, config, children) {
|
||||
var owner = element._owner;
|
||||
|
||||
if (config != null) {
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
/* eslint-disable no-proto */
|
||||
config.__proto__ == null || config.__proto__ === Object.prototype,
|
||||
/* eslint-enable no-proto */
|
||||
'React.cloneElement(...): Expected props argument to be a plain object. ' +
|
||||
'Properties defined in its prototype chain will be ignored.'
|
||||
);
|
||||
}
|
||||
if (config.ref !== undefined) {
|
||||
// Silently steal the ref from the parent.
|
||||
ref = config.ref;
|
||||
@@ -319,6 +347,8 @@ ReactElement.cloneElement = function(element, config, children) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Verifies the object is a ReactElement.
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.isvalidelement
|
||||
* @param {?object} object
|
||||
* @return {boolean} True if `object` is a valid component.
|
||||
* @final
|
||||
|
||||
@@ -138,6 +138,18 @@ describe('ReactElement', function() {
|
||||
expect(element.props.foo).toBe(1);
|
||||
});
|
||||
|
||||
it('warns if the config object inherits from any type other than Object', function() {
|
||||
spyOn(console, 'error');
|
||||
React.createElement('div', {foo: 1});
|
||||
expect(console.error).not.toHaveBeenCalled();
|
||||
React.createElement('div', Object.create({foo: 1}));
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'React.createElement(...): Expected props argument to be a plain object. ' +
|
||||
'Properties defined in its prototype chain will be ignored.'
|
||||
);
|
||||
});
|
||||
|
||||
it('extracts key and ref from the config', function() {
|
||||
var element = React.createFactory(ComponentClass)({
|
||||
key: '12',
|
||||
|
||||
@@ -66,6 +66,18 @@ describe('ReactElementClone', function() {
|
||||
expect(ReactDOM.findDOMNode(component).childNodes[0].className).toBe('xyz');
|
||||
});
|
||||
|
||||
it('should warn if the config object inherits from any type other than Object', function() {
|
||||
spyOn(console, 'error');
|
||||
React.cloneElement('div', {foo: 1});
|
||||
expect(console.error).not.toHaveBeenCalled();
|
||||
React.cloneElement('div', Object.create({foo: 1}));
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'React.cloneElement(...): Expected props argument to be a plain object. ' +
|
||||
'Properties defined in its prototype chain will be ignored.'
|
||||
);
|
||||
});
|
||||
|
||||
it('should keep the original ref if it is not overridden', function() {
|
||||
var Grandparent = React.createClass({
|
||||
render: function() {
|
||||
|
||||
@@ -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;
|
||||
163
src/isomorphic/devtools/ReactComponentTreeDevtool.js
Normal file
163
src/isomorphic/devtools/ReactComponentTreeDevtool.js
Normal file
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* Copyright 2016-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 ReactComponentTreeDevtool
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
var tree = {};
|
||||
var rootIDs = [];
|
||||
|
||||
function updateTree(id, update) {
|
||||
if (!tree[id]) {
|
||||
tree[id] = {
|
||||
parentID: null,
|
||||
ownerID: null,
|
||||
text: null,
|
||||
childIDs: [],
|
||||
displayName: 'Unknown',
|
||||
isMounted: false,
|
||||
updateCount: 0,
|
||||
};
|
||||
}
|
||||
update(tree[id]);
|
||||
}
|
||||
|
||||
function purgeDeep(id) {
|
||||
var item = tree[id];
|
||||
if (item) {
|
||||
var {childIDs} = item;
|
||||
delete tree[id];
|
||||
childIDs.forEach(purgeDeep);
|
||||
}
|
||||
}
|
||||
|
||||
var ReactComponentTreeDevtool = {
|
||||
onSetDisplayName(id, displayName) {
|
||||
updateTree(id, item => item.displayName = displayName);
|
||||
},
|
||||
|
||||
onSetChildren(id, nextChildIDs) {
|
||||
updateTree(id, item => {
|
||||
var prevChildIDs = item.childIDs;
|
||||
item.childIDs = nextChildIDs;
|
||||
|
||||
nextChildIDs.forEach(nextChildID => {
|
||||
var nextChild = tree[nextChildID];
|
||||
invariant(
|
||||
nextChild,
|
||||
'Expected devtool events to fire for the child ' +
|
||||
'before its parent includes it in onSetChildren().'
|
||||
);
|
||||
invariant(
|
||||
nextChild.displayName != null,
|
||||
'Expected onSetDisplayName() to fire for the child ' +
|
||||
'before its parent includes it in onSetChildren().'
|
||||
);
|
||||
invariant(
|
||||
nextChild.childIDs != null || nextChild.text != null,
|
||||
'Expected onSetChildren() or onSetText() to fire for the child ' +
|
||||
'before its parent includes it in onSetChildren().'
|
||||
);
|
||||
invariant(
|
||||
nextChild.isMounted,
|
||||
'Expected onMountComponent() to fire for the child ' +
|
||||
'before its parent includes it in onSetChildren().'
|
||||
);
|
||||
|
||||
if (prevChildIDs.indexOf(nextChildID) === -1) {
|
||||
nextChild.parentID = id;
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
onSetOwner(id, ownerID) {
|
||||
updateTree(id, item => item.ownerID = ownerID);
|
||||
},
|
||||
|
||||
onSetText(id, text) {
|
||||
updateTree(id, item => item.text = text);
|
||||
},
|
||||
|
||||
onMountComponent(id) {
|
||||
updateTree(id, item => item.isMounted = true);
|
||||
},
|
||||
|
||||
onMountRootComponent(id) {
|
||||
rootIDs.push(id);
|
||||
},
|
||||
|
||||
onUpdateComponent(id) {
|
||||
updateTree(id, item => item.updateCount++);
|
||||
},
|
||||
|
||||
onUnmountComponent(id) {
|
||||
updateTree(id, item => item.isMounted = false);
|
||||
rootIDs = rootIDs.filter(rootID => rootID !== id);
|
||||
},
|
||||
|
||||
purgeUnmountedComponents() {
|
||||
if (ReactComponentTreeDevtool._preventPurging) {
|
||||
// Should only be used for testing.
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(tree)
|
||||
.filter(id => !tree[id].isMounted)
|
||||
.forEach(purgeDeep);
|
||||
},
|
||||
|
||||
isMounted(id) {
|
||||
var item = tree[id];
|
||||
return item ? item.isMounted : false;
|
||||
},
|
||||
|
||||
getChildIDs(id) {
|
||||
var item = tree[id];
|
||||
return item ? item.childIDs : [];
|
||||
},
|
||||
|
||||
getDisplayName(id) {
|
||||
var item = tree[id];
|
||||
return item ? item.displayName : 'Unknown';
|
||||
},
|
||||
|
||||
getOwnerID(id) {
|
||||
var item = tree[id];
|
||||
return item ? item.ownerID : null;
|
||||
},
|
||||
|
||||
getParentID(id) {
|
||||
var item = tree[id];
|
||||
return item ? item.parentID : null;
|
||||
},
|
||||
|
||||
getText(id) {
|
||||
var item = tree[id];
|
||||
return item ? item.text : null;
|
||||
},
|
||||
|
||||
getUpdateCount(id) {
|
||||
var item = tree[id];
|
||||
return item ? item.updateCount : 0;
|
||||
},
|
||||
|
||||
getRootIDs() {
|
||||
return rootIDs;
|
||||
},
|
||||
|
||||
getRegisteredIDs() {
|
||||
return Object.keys(tree);
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = ReactComponentTreeDevtool;
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright 2016-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 ReactNativeOperationHistoryDevtool
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var history = [];
|
||||
|
||||
var ReactNativeOperationHistoryDevtool = {
|
||||
onNativeOperation(debugID, type, payload) {
|
||||
history.push({
|
||||
instanceID: debugID,
|
||||
type,
|
||||
payload,
|
||||
});
|
||||
},
|
||||
|
||||
clearHistory() {
|
||||
if (ReactNativeOperationHistoryDevtool._preventClearing) {
|
||||
// Should only be used for tests.
|
||||
return;
|
||||
}
|
||||
|
||||
history = [];
|
||||
},
|
||||
|
||||
getHistory() {
|
||||
return history;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = ReactNativeOperationHistoryDevtool;
|
||||
1744
src/isomorphic/devtools/__tests__/ReactComponentTreeDevtool-test.js
Normal file
1744
src/isomorphic/devtools/__tests__/ReactComponentTreeDevtool-test.js
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,724 @@
|
||||
/**
|
||||
* Copyright 2016-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('ReactNativeOperationHistoryDevtool', () => {
|
||||
var React;
|
||||
var ReactDOM;
|
||||
var ReactDOMComponentTree;
|
||||
var ReactDOMFeatureFlags;
|
||||
var ReactNativeOperationHistoryDevtool;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModuleRegistry();
|
||||
|
||||
React = require('React');
|
||||
ReactDOM = require('ReactDOM');
|
||||
ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
|
||||
ReactNativeOperationHistoryDevtool = require('ReactNativeOperationHistoryDevtool');
|
||||
});
|
||||
|
||||
function assertHistoryMatches(expectedHistory) {
|
||||
var actualHistory = ReactNativeOperationHistoryDevtool.getHistory();
|
||||
expect(actualHistory).toEqual(expectedHistory);
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
it('gets recorded for native roots', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div><p>Hi.</p></div>, node);
|
||||
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: ReactDOMFeatureFlags.useCreateElement ?
|
||||
'DIV' :
|
||||
'<div data-reactroot="" data-reactid="1"><p data-reactid="2">Hi.</p></div>',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets recorded for composite roots', () => {
|
||||
function Foo() {
|
||||
return <div><p>Hi.</p></div>;
|
||||
}
|
||||
var node = document.createElement('div');
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: ReactDOMFeatureFlags.useCreateElement ?
|
||||
'DIV' :
|
||||
'<div data-reactroot="" data-reactid="1">' +
|
||||
'<p data-reactid="2">Hi.</p></div>',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets ignored for composite roots that return null', () => {
|
||||
function Foo() {
|
||||
return null;
|
||||
}
|
||||
var node = document.createElement('div');
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
// Empty DOM components should be invisible to devtools.
|
||||
assertHistoryMatches([]);
|
||||
});
|
||||
|
||||
it('gets recorded when a native is mounted deeply instead of null', () => {
|
||||
var element;
|
||||
function Foo() {
|
||||
return element;
|
||||
}
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
|
||||
var node = document.createElement('div');
|
||||
element = null;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
element = <span />;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
// Since empty components should be invisible to devtools,
|
||||
// we record a "mount" event rather than a "replace with".
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: 'SPAN',
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('update styles', () => {
|
||||
it('gets recorded during mount', () => {
|
||||
var node = document.createElement('div');
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div style={{
|
||||
color: 'red',
|
||||
backgroundColor: 'yellow',
|
||||
}} />, node);
|
||||
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
if (ReactDOMFeatureFlags.useCreateElement) {
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update styles',
|
||||
payload: {
|
||||
color: 'red',
|
||||
backgroundColor: 'yellow',
|
||||
},
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: 'DIV',
|
||||
}]);
|
||||
} else {
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: '<div style="color:red;background-color:yellow;" ' +
|
||||
'data-reactroot="" data-reactid="1"></div>',
|
||||
}]);
|
||||
}
|
||||
});
|
||||
|
||||
it('gets recorded during an update', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div style={{ color: 'red' }} />, node);
|
||||
ReactDOM.render(<div style={{
|
||||
color: 'blue',
|
||||
backgroundColor: 'yellow',
|
||||
}} />, node);
|
||||
ReactDOM.render(<div style={{ backgroundColor: 'green' }} />, node);
|
||||
ReactDOM.render(<div />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update styles',
|
||||
payload: { color: 'red' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update styles',
|
||||
payload: { color: 'blue', backgroundColor: 'yellow' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update styles',
|
||||
payload: { color: '', backgroundColor: 'green' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update styles',
|
||||
payload: { backgroundColor: '' },
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets ignored if the styles are shallowly equal', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div style={{
|
||||
color: 'red',
|
||||
backgroundColor: 'yellow',
|
||||
}} />, node);
|
||||
ReactDOM.render(<div style={{
|
||||
color: 'red',
|
||||
backgroundColor: 'yellow',
|
||||
}} />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update styles',
|
||||
payload: {
|
||||
color: 'red',
|
||||
backgroundColor: 'yellow',
|
||||
},
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('update attribute', () => {
|
||||
describe('simple attribute', () => {
|
||||
it('gets recorded during mount', () => {
|
||||
var node = document.createElement('div');
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div className="rad" tabIndex={42} />, node);
|
||||
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
if (ReactDOMFeatureFlags.useCreateElement) {
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { className: 'rad' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { tabIndex: 42 },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: 'DIV',
|
||||
}]);
|
||||
} else {
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: '<div class="rad" tabindex="42" data-reactroot="" ' +
|
||||
'data-reactid="1"></div>',
|
||||
}]);
|
||||
}
|
||||
});
|
||||
|
||||
it('gets recorded during an update', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div className="rad" />, node);
|
||||
ReactDOM.render(<div className="mad" tabIndex={42} />, node);
|
||||
ReactDOM.render(<div tabIndex={43} />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { className: 'rad' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { className: 'mad' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { tabIndex: 42 },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove attribute',
|
||||
payload: 'className',
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { tabIndex: 43 },
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('attribute that gets removed with certain values', () => {
|
||||
it('gets recorded as a removal during an update', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div disabled={true} />, node);
|
||||
ReactDOM.render(<div disabled={false} />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { disabled: true },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove attribute',
|
||||
payload: 'disabled',
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('custom attribute', () => {
|
||||
it('gets recorded during mount', () => {
|
||||
var node = document.createElement('div');
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div data-x="rad" data-y={42} />, node);
|
||||
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
if (ReactDOMFeatureFlags.useCreateElement) {
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { 'data-x': 'rad' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { 'data-y': 42 },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: 'DIV',
|
||||
}]);
|
||||
} else {
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: '<div data-x="rad" data-y="42" data-reactroot="" ' +
|
||||
'data-reactid="1"></div>',
|
||||
}]);
|
||||
}
|
||||
});
|
||||
|
||||
it('gets recorded during an update', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div data-x="rad" />, node);
|
||||
ReactDOM.render(<div data-x="mad" data-y={42} />, node);
|
||||
ReactDOM.render(<div data-y={43} />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { 'data-x': 'rad' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { 'data-x': 'mad' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { 'data-y': 42 },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove attribute',
|
||||
payload: 'data-x',
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { 'data-y': 43 },
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('attribute on a web component', () => {
|
||||
it('gets recorded during mount', () => {
|
||||
var node = document.createElement('div');
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<my-component className="rad" tabIndex={42} />, node);
|
||||
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
if (ReactDOMFeatureFlags.useCreateElement) {
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { className: 'rad' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { tabIndex: 42 },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: 'MY-COMPONENT',
|
||||
}]);
|
||||
} else {
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: '<my-component className="rad" tabIndex="42" ' +
|
||||
'data-reactroot="" data-reactid="1"></my-component>',
|
||||
}]);
|
||||
}
|
||||
});
|
||||
|
||||
it('gets recorded during an update', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<my-component />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<my-component className="rad" />, node);
|
||||
ReactDOM.render(<my-component className="mad" tabIndex={42} />, node);
|
||||
ReactDOM.render(<my-component tabIndex={43} />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { className: 'rad' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { className: 'mad' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { tabIndex: 42 },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove attribute',
|
||||
payload: 'className',
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { tabIndex: 43 },
|
||||
}]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('replace text', () => {
|
||||
describe('text content', () => {
|
||||
it('gets recorded during an update from text content', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div>Hi.</div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div>Bye.</div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace text',
|
||||
payload: 'Bye.',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets recorded during an update from html', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div dangerouslySetInnerHTML={{__html: 'Hi.'}} />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div>Bye.</div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace text',
|
||||
payload: 'Bye.',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets recorded during an update from children', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div><span /><p /></div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div>Bye.</div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove child',
|
||||
payload: {fromIndex: 0},
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove child',
|
||||
payload: {fromIndex: 1},
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace text',
|
||||
payload: 'Bye.',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets ignored if new text is equal', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div>Hi.</div>, node);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div>Hi.</div>, node);
|
||||
|
||||
assertHistoryMatches([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('text node', () => {
|
||||
it('gets recorded during an update', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div>{'Hi.'}{42}</div>, node);
|
||||
var inst1 = ReactDOMComponentTree.getInstanceFromNode(node.firstChild.childNodes[0]);
|
||||
var inst2 = ReactDOMComponentTree.getInstanceFromNode(node.firstChild.childNodes[3]);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div>{'Bye.'}{43}</div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst1._debugID,
|
||||
type: 'replace text',
|
||||
payload: 'Bye.',
|
||||
}, {
|
||||
instanceID: inst2._debugID,
|
||||
type: 'replace text',
|
||||
payload: '43',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets ignored if new text is equal', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div>{'Hi.'}{42}</div>, node);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div>{'Hi.'}{42}</div>, node);
|
||||
|
||||
assertHistoryMatches([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('replace with', () => {
|
||||
it('gets recorded when composite renders to a different type', () => {
|
||||
var element;
|
||||
function Foo() {
|
||||
return element;
|
||||
}
|
||||
|
||||
var node = document.createElement('div');
|
||||
element = <div />;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
element = <span />;
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace with',
|
||||
payload: 'SPAN',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets recorded when composite renders to null after a native', () => {
|
||||
var element;
|
||||
function Foo() {
|
||||
return element;
|
||||
}
|
||||
|
||||
var node = document.createElement('div');
|
||||
element = <span />;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
element = null;
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace with',
|
||||
payload: '#comment',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets ignored if the type has not changed', () => {
|
||||
var element;
|
||||
function Foo() {
|
||||
return element;
|
||||
}
|
||||
|
||||
var node = document.createElement('div');
|
||||
element = <div />;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
element = <div />;
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
assertHistoryMatches([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('replace children', () => {
|
||||
it('gets recorded during an update from text content', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div>Hi.</div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(
|
||||
<div dangerouslySetInnerHTML={{__html: 'Bye.'}} />,
|
||||
node
|
||||
);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace children',
|
||||
payload: 'Bye.',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets recorded during an update from html', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(
|
||||
<div dangerouslySetInnerHTML={{__html: 'Hi.'}} />,
|
||||
node
|
||||
);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(
|
||||
<div dangerouslySetInnerHTML={{__html: 'Bye.'}} />,
|
||||
node
|
||||
);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace children',
|
||||
payload: 'Bye.',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets recorded during an update from children', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div><span /><p /></div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(
|
||||
<div dangerouslySetInnerHTML={{__html: 'Hi.'}} />,
|
||||
node
|
||||
);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove child',
|
||||
payload: {fromIndex: 0},
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove child',
|
||||
payload: {fromIndex: 1},
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace children',
|
||||
payload: 'Hi.',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets ignored if new html is equal', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(
|
||||
<div dangerouslySetInnerHTML={{__html: 'Hi.'}} />,
|
||||
node
|
||||
);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(
|
||||
<div dangerouslySetInnerHTML={{__html: 'Hi.'}} />,
|
||||
node
|
||||
);
|
||||
|
||||
assertHistoryMatches([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('insert child', () => {
|
||||
it('gets reported when a child is inserted', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div><span /></div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div><span /><p /></div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'insert child',
|
||||
payload: {toIndex: 1, content: 'P'},
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('move child', () => {
|
||||
it('gets reported when a child is inserted', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div><span key="a" /><p key="b" /></div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div><p key="b" /><span key="a" /></div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'move child',
|
||||
payload: {fromIndex: 0, toIndex: 1},
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove child', () => {
|
||||
it('gets reported when a child is removed', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div><span key="a" /><p key="b" /></div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div><span key="a" /></div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove child',
|
||||
payload: {fromIndex: 1},
|
||||
}]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "react-haste",
|
||||
"version": "15.0.0-rc.2",
|
||||
"version": "15.0.1",
|
||||
"license": "BSD-3-Clause"
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
var ReactDefaultInjection = require('ReactDefaultInjection');
|
||||
var ReactMount = require('ReactMount');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var ReactReconciler = require('ReactReconciler');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
var ReactVersion = require('ReactVersion');
|
||||
@@ -28,11 +27,9 @@ var warning = require('warning');
|
||||
|
||||
ReactDefaultInjection.inject();
|
||||
|
||||
var render = ReactPerf.measure('React', 'render', ReactMount.render);
|
||||
|
||||
var React = {
|
||||
findDOMNode: findDOMNode,
|
||||
render: render,
|
||||
render: ReactMount.render,
|
||||
unmountComponentAtNode: ReactMount.unmountComponentAtNode,
|
||||
version: ReactVersion,
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
var DOMChildrenOperations = require('DOMChildrenOperations');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
|
||||
/**
|
||||
* Operations used to process updates to DOM nodes.
|
||||
@@ -32,8 +31,4 @@ var ReactDOMIDOperations = {
|
||||
},
|
||||
};
|
||||
|
||||
ReactPerf.measureMethods(ReactDOMIDOperations, 'ReactDOMIDOperations', {
|
||||
dangerouslyProcessChildrenUpdates: 'dangerouslyProcessChildrenUpdates',
|
||||
});
|
||||
|
||||
module.exports = ReactDOMIDOperations;
|
||||
|
||||
@@ -22,7 +22,6 @@ var ReactElement = require('ReactElement');
|
||||
var ReactFeatureFlags = require('ReactFeatureFlags');
|
||||
var ReactInstrumentation = require('ReactInstrumentation');
|
||||
var ReactMarkupChecksum = require('ReactMarkupChecksum');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var ReactReconciler = require('ReactReconciler');
|
||||
var ReactUpdateQueue = require('ReactUpdateQueue');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
@@ -308,6 +307,10 @@ var ReactMount = {
|
||||
shouldReuseMarkup,
|
||||
context
|
||||
) {
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onBeginFlush();
|
||||
}
|
||||
|
||||
// Various parts of our code (such as ReactCompositeComponent's
|
||||
// _renderValidatedComponent) assume that calls to render aren't nested;
|
||||
// verify that that's the case.
|
||||
@@ -333,6 +336,12 @@ var ReactMount = {
|
||||
ReactBrowserEventEmitter.ensureScrollValueMonitoring();
|
||||
var componentInstance = instantiateReactComponent(nextElement);
|
||||
|
||||
if (__DEV__) {
|
||||
// Mute future events from the top level wrapper.
|
||||
// It is an implementation detail that devtools should not know about.
|
||||
componentInstance._debugID = 0;
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -349,7 +358,11 @@ var ReactMount = {
|
||||
instancesByReactRootID[wrapperID] = componentInstance;
|
||||
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onMountRootComponent(componentInstance);
|
||||
// The instance here is TopLevelWrapper so we report mount for its child.
|
||||
ReactInstrumentation.debugTool.onMountRootComponent(
|
||||
componentInstance._renderedComponent._debugID
|
||||
);
|
||||
ReactInstrumentation.debugTool.onEndFlush();
|
||||
}
|
||||
|
||||
return componentInstance;
|
||||
@@ -497,6 +510,7 @@ var ReactMount = {
|
||||
|
||||
/**
|
||||
* Renders a React component into the DOM in the supplied `container`.
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#reactdom.render
|
||||
*
|
||||
* If the React component was previously rendered into `container`, this will
|
||||
* perform an update on it and only mutate the DOM as necessary to reflect the
|
||||
@@ -513,6 +527,7 @@ var ReactMount = {
|
||||
|
||||
/**
|
||||
* Unmounts and destroys the React component rendered in the `container`.
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#reactdom.unmountcomponentatnode
|
||||
*
|
||||
* @param {DOMElement} container DOM element containing a React component.
|
||||
* @return {boolean} True if a component was found in and unmounted from
|
||||
@@ -684,12 +699,18 @@ var ReactMount = {
|
||||
setInnerHTML(container, markup);
|
||||
ReactDOMComponentTree.precacheNode(instance, container.firstChild);
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
var nativeNode = ReactDOMComponentTree.getInstanceFromNode(container.firstChild);
|
||||
if (nativeNode._debugID !== 0) {
|
||||
ReactInstrumentation.debugTool.onNativeOperation(
|
||||
nativeNode._debugID,
|
||||
'mount',
|
||||
markup.toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
ReactPerf.measureMethods(ReactMount, 'ReactMount', {
|
||||
_renderNewRootComponent: '_renderNewRootComponent',
|
||||
_mountImageIntoNode: '_mountImageIntoNode',
|
||||
});
|
||||
|
||||
module.exports = ReactMount;
|
||||
|
||||
@@ -35,7 +35,7 @@ var recordIDAndReturnFalse = function(id, event) {
|
||||
recordID(id);
|
||||
return false;
|
||||
};
|
||||
var LISTENER = jest.genMockFn();
|
||||
var LISTENER = jest.fn();
|
||||
var ON_CLICK_KEY = keyOf({onClick: null});
|
||||
var ON_TOUCH_TAP_KEY = keyOf({onTouchTap: null});
|
||||
var ON_CHANGE_KEY = keyOf({onChange: null});
|
||||
@@ -284,7 +284,7 @@ describe('ReactBrowserEventEmitter', function() {
|
||||
*/
|
||||
|
||||
it('should invoke handlers that were removed while bubbling', function() {
|
||||
var handleParentClick = jest.genMockFn();
|
||||
var handleParentClick = jest.fn();
|
||||
var handleChildClick = function(event) {
|
||||
EventPluginHub.deleteAllListeners(getInternal(PARENT));
|
||||
};
|
||||
@@ -303,7 +303,7 @@ describe('ReactBrowserEventEmitter', function() {
|
||||
});
|
||||
|
||||
it('should not invoke newly inserted handlers while bubbling', function() {
|
||||
var handleParentClick = jest.genMockFn();
|
||||
var handleParentClick = jest.fn();
|
||||
var handleChildClick = function(event) {
|
||||
EventPluginHub.putListener(
|
||||
getInternal(PARENT),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user