react 0.14.0-beta2 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +4 -4
  2. package/addons.js +3 -1
  3. package/dist/react-with-addons.js +1944 -1375
  4. package/dist/react-with-addons.min.js +6 -6
  5. package/dist/react.js +1704 -1250
  6. package/dist/react.min.js +6 -6
  7. package/lib/CSSProperty.js +15 -3
  8. package/lib/CSSPropertyOperations.js +15 -2
  9. package/lib/ChangeEventPlugin.js +5 -2
  10. package/lib/DOMChildrenOperations.js +12 -1
  11. package/lib/DOMPropertyOperations.js +14 -1
  12. package/lib/Danger.js +9 -4
  13. package/lib/EnterLeaveEventPlugin.js +13 -5
  14. package/lib/EventConstants.js +2 -2
  15. package/lib/EventPluginHub.js +18 -10
  16. package/lib/EventPluginUtils.js +23 -27
  17. package/lib/EventPropagators.js +1 -1
  18. package/lib/FallbackCompositionState.js +6 -0
  19. package/lib/HTMLDOMPropertyConfig.js +25 -2
  20. package/lib/PooledClass.js +1 -3
  21. package/lib/React.js +14 -3
  22. package/lib/ReactBrowserComponentMixin.js +1 -1
  23. package/lib/ReactBrowserEventEmitter.js +10 -4
  24. package/lib/ReactCSSTransitionGroup.js +33 -18
  25. package/lib/ReactCSSTransitionGroupChild.js +42 -25
  26. package/lib/ReactChildReconciler.js +3 -5
  27. package/lib/ReactChildren.js +70 -30
  28. package/lib/ReactClass.js +6 -6
  29. package/lib/ReactComponent.js +7 -6
  30. package/lib/ReactCompositeComponent.js +58 -7
  31. package/lib/ReactDOM.js +7 -5
  32. package/lib/ReactDOMComponent.js +140 -46
  33. package/lib/ReactDOMFeatureFlags.js +18 -0
  34. package/lib/ReactDOMIDOperations.js +1 -60
  35. package/lib/ReactDOMInput.js +10 -1
  36. package/lib/ReactDOMSelect.js +1 -1
  37. package/lib/ReactDOMSelection.js +16 -0
  38. package/lib/ReactDOMServer.js +3 -1
  39. package/lib/ReactDOMTextComponent.js +23 -10
  40. package/lib/ReactDOMTextarea.js +3 -1
  41. package/lib/ReactDefaultInjection.js +0 -2
  42. package/lib/ReactDefaultPerf.js +10 -4
  43. package/lib/ReactDefaultPerfAnalysis.js +7 -3
  44. package/lib/ReactElement.js +72 -35
  45. package/lib/ReactElementValidator.js +31 -75
  46. package/lib/ReactEmptyComponent.js +25 -61
  47. package/lib/ReactEmptyComponentRegistry.js +48 -0
  48. package/lib/ReactErrorUtils.js +55 -9
  49. package/lib/ReactEventEmitterMixin.js +1 -1
  50. package/lib/ReactEventListener.js +16 -9
  51. package/lib/ReactFragment.js +25 -116
  52. package/lib/ReactInjection.js +0 -2
  53. package/lib/ReactIsomorphic.js +4 -0
  54. package/lib/ReactLink.js +1 -1
  55. package/lib/ReactMount.js +127 -41
  56. package/lib/ReactMultiChild.js +37 -4
  57. package/lib/ReactOwner.js +2 -2
  58. package/lib/ReactPropTransferer.js +1 -1
  59. package/lib/ReactPropTypes.js +11 -8
  60. package/lib/ReactReconcileTransaction.js +4 -2
  61. package/lib/ReactReconciler.js +16 -17
  62. package/lib/ReactRef.js +13 -1
  63. package/lib/ReactServerRenderingTransaction.js +1 -0
  64. package/lib/ReactTestUtils.js +27 -15
  65. package/lib/ReactTransitionChildMapping.js +3 -6
  66. package/lib/ReactUpdateQueue.js +4 -4
  67. package/lib/ReactUpdates.js +1 -1
  68. package/lib/ReactVersion.js +14 -0
  69. package/lib/ReactWithAddons.js +10 -1
  70. package/lib/ResponderEventPlugin.js +1 -1
  71. package/lib/SelectEventPlugin.js +11 -1
  72. package/lib/SimpleEventPlugin.js +10 -31
  73. package/lib/SyntheticEvent.js +15 -1
  74. package/lib/Transaction.js +1 -1
  75. package/lib/canDefineProperty.js +24 -0
  76. package/lib/createHierarchyRenderer.js +1 -1
  77. package/lib/deprecated.js +3 -2
  78. package/lib/findDOMNode.js +1 -1
  79. package/lib/getTestDocument.js +4 -11
  80. package/lib/instantiateReactComponent.js +3 -5
  81. package/lib/reactComponentExpect.js +6 -0
  82. package/lib/shouldUpdateReactComponent.js +12 -8
  83. package/lib/sliceChildren.js +3 -20
  84. package/lib/traverseAllChildren.js +15 -9
  85. package/package.json +2 -2
  86. package/react.js +1 -51
  87. package/dist/JSXTransformer.js +0 -17949
  88. package/lib/joinClasses.js +0 -39
  89. package/lib/memoizeStringOnly.js +0 -31
@@ -12,13 +12,18 @@
12
12
  'use strict';
13
13
 
14
14
  var PooledClass = require('./PooledClass');
15
- var ReactFragment = require('./ReactFragment');
15
+ var ReactElement = require('./ReactElement');
16
16
 
17
+ var emptyFunction = require('fbjs/lib/emptyFunction');
17
18
  var traverseAllChildren = require('./traverseAllChildren');
18
- var warning = require('fbjs/lib/warning');
19
19
 
20
20
  var twoArgumentPooler = PooledClass.twoArgumentPooler;
21
- var threeArgumentPooler = PooledClass.threeArgumentPooler;
21
+ var fourArgumentPooler = PooledClass.fourArgumentPooler;
22
+
23
+ var userProvidedKeyEscapeRegex = /\/(?!\/)/g;
24
+ function escapeUserProvidedKey(text) {
25
+ return ('' + text).replace(userProvidedKeyEscapeRegex, '//');
26
+ }
22
27
 
23
28
  /**
24
29
  * PooledClass representing the bookkeeping associated with performing a child
@@ -33,11 +38,18 @@ function ForEachBookKeeping(forEachFunction, forEachContext) {
33
38
  this.context = forEachContext;
34
39
  this.count = 0;
35
40
  }
41
+ ForEachBookKeeping.prototype.destructor = function () {
42
+ this.func = null;
43
+ this.context = null;
44
+ this.count = 0;
45
+ };
36
46
  PooledClass.addPoolingTo(ForEachBookKeeping, twoArgumentPooler);
37
47
 
38
- function forEachSingleChild(traverseContext, child, name) {
39
- var bookKeeping = traverseContext;
40
- bookKeeping.func.call(bookKeeping.context, child, bookKeeping.count++);
48
+ function forEachSingleChild(bookKeeping, child, name) {
49
+ var func = bookKeeping.func;
50
+ var context = bookKeeping.context;
51
+
52
+ func.call(context, child, bookKeeping.count++);
41
53
  }
42
54
 
43
55
  /**
@@ -54,7 +66,6 @@ function forEachChildren(children, forEachFunc, forEachContext) {
54
66
  if (children == null) {
55
67
  return children;
56
68
  }
57
-
58
69
  var traverseContext = ForEachBookKeeping.getPooled(forEachFunc, forEachContext);
59
70
  traverseAllChildren(children, forEachSingleChild, traverseContext);
60
71
  ForEachBookKeeping.release(traverseContext);
@@ -69,27 +80,50 @@ function forEachChildren(children, forEachFunc, forEachContext) {
69
80
  * @param {!function} mapFunction Function to perform mapping with.
70
81
  * @param {?*} mapContext Context to perform mapping with.
71
82
  */
72
- function MapBookKeeping(mapResult, mapFunction, mapContext) {
83
+ function MapBookKeeping(mapResult, keyPrefix, mapFunction, mapContext) {
73
84
  this.result = mapResult;
85
+ this.keyPrefix = keyPrefix;
74
86
  this.func = mapFunction;
75
87
  this.context = mapContext;
76
88
  this.count = 0;
77
89
  }
78
- PooledClass.addPoolingTo(MapBookKeeping, threeArgumentPooler);
79
-
80
- function mapSingleChildIntoContext(traverseContext, child, name) {
81
- var bookKeeping = traverseContext;
82
- var mapResult = bookKeeping.result;
83
-
84
- var keyUnique = mapResult[name] === undefined;
85
- if (process.env.NODE_ENV !== 'production') {
86
- process.env.NODE_ENV !== 'production' ? warning(keyUnique, 'ReactChildren.map(...): Encountered two children with the same key, ' + '`%s`. Child keys must be unique; when two children share a key, only ' + 'the first child will be used.', name) : undefined;
90
+ MapBookKeeping.prototype.destructor = function () {
91
+ this.result = null;
92
+ this.keyPrefix = null;
93
+ this.func = null;
94
+ this.context = null;
95
+ this.count = 0;
96
+ };
97
+ PooledClass.addPoolingTo(MapBookKeeping, fourArgumentPooler);
98
+
99
+ function mapSingleChildIntoContext(bookKeeping, child, childKey) {
100
+ var result = bookKeeping.result;
101
+ var keyPrefix = bookKeeping.keyPrefix;
102
+ var func = bookKeeping.func;
103
+ var context = bookKeeping.context;
104
+
105
+ var mappedChild = func.call(context, child, bookKeeping.count++);
106
+ if (Array.isArray(mappedChild)) {
107
+ mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, emptyFunction.thatReturnsArgument);
108
+ } else if (mappedChild != null) {
109
+ if (ReactElement.isValidElement(mappedChild)) {
110
+ mappedChild = ReactElement.cloneAndReplaceKey(mappedChild,
111
+ // Keep both the (mapped) and old keys if they differ, just as
112
+ // traverseAllChildren used to do for objects as children
113
+ keyPrefix + (mappedChild !== child ? escapeUserProvidedKey(mappedChild.key || '') + '/' : '') + childKey);
114
+ }
115
+ result.push(mappedChild);
87
116
  }
117
+ }
88
118
 
89
- if (keyUnique) {
90
- var mappedChild = bookKeeping.func.call(bookKeeping.context, child, bookKeeping.count++);
91
- mapResult[name] = mappedChild;
119
+ function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
120
+ var escapedPrefix = '';
121
+ if (prefix != null) {
122
+ escapedPrefix = escapeUserProvidedKey(prefix) + '/';
92
123
  }
124
+ var traverseContext = MapBookKeeping.getPooled(array, escapedPrefix, func, context);
125
+ traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
126
+ MapBookKeeping.release(traverseContext);
93
127
  }
94
128
 
95
129
  /**
@@ -98,9 +132,6 @@ function mapSingleChildIntoContext(traverseContext, child, name) {
98
132
  * The provided mapFunction(child, key, index) will be called for each
99
133
  * leaf child.
100
134
  *
101
- * TODO: This may likely break any calls to `ReactChildren.map` that were
102
- * previously relying on the fact that we guarded against null children.
103
- *
104
135
  * @param {?*} children Children tree container.
105
136
  * @param {function(*, int)} func The map function.
106
137
  * @param {*} context Context for mapFunction.
@@ -110,12 +141,9 @@ function mapChildren(children, func, context) {
110
141
  if (children == null) {
111
142
  return children;
112
143
  }
113
-
114
- var mapResult = {};
115
- var traverseContext = MapBookKeeping.getPooled(mapResult, func, context);
116
- traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
117
- MapBookKeeping.release(traverseContext);
118
- return ReactFragment.create(mapResult);
144
+ var result = [];
145
+ mapIntoWithKeyPrefixInternal(children, result, null, func, context);
146
+ return result;
119
147
  }
120
148
 
121
149
  function forEachSingleChildDummy(traverseContext, child, name) {
@@ -133,10 +161,22 @@ function countChildren(children, context) {
133
161
  return traverseAllChildren(children, forEachSingleChildDummy, null);
134
162
  }
135
163
 
164
+ /**
165
+ * Flatten a children object (typically specified as `props.children`) and
166
+ * return an array with appropriately re-keyed children.
167
+ */
168
+ function toArray(children) {
169
+ var result = [];
170
+ mapIntoWithKeyPrefixInternal(children, result, null, emptyFunction.thatReturnsArgument);
171
+ return result;
172
+ }
173
+
136
174
  var ReactChildren = {
137
175
  forEach: forEachChildren,
138
176
  map: mapChildren,
139
- count: countChildren
177
+ mapIntoWithKeyPrefixInternal: mapIntoWithKeyPrefixInternal,
178
+ count: countChildren,
179
+ toArray: toArray
140
180
  };
141
181
 
142
182
  module.exports = ReactChildren;
package/lib/ReactClass.js CHANGED
@@ -13,7 +13,6 @@
13
13
 
14
14
  var ReactComponent = require('./ReactComponent');
15
15
  var ReactElement = require('./ReactElement');
16
- var ReactErrorUtils = require('./ReactErrorUtils');
17
16
  var ReactPropTypeLocations = require('./ReactPropTypeLocations');
18
17
  var ReactPropTypeLocationNames = require('./ReactPropTypeLocationNames');
19
18
  var ReactNoopUpdateQueue = require('./ReactNoopUpdateQueue');
@@ -58,7 +57,7 @@ var warnedSetProps = false;
58
57
  function warnSetProps() {
59
58
  if (!warnedSetProps) {
60
59
  warnedSetProps = true;
61
- process.env.NODE_ENV !== 'production' ? warning(false, 'setProps(...) and replaceProps(...) are deprecated. ' + 'Instead, call React.render again at the top level.') : undefined;
60
+ process.env.NODE_ENV !== 'production' ? warning(false, 'setProps(...) and replaceProps(...) are deprecated. ' + 'Instead, call render again at the top level.') : undefined;
62
61
  }
63
62
  }
64
63
 
@@ -358,9 +357,10 @@ var RESERVED_SPEC_KEYS = {
358
357
  },
359
358
  statics: function (Constructor, statics) {
360
359
  mixStaticSpecIntoComponent(Constructor, statics);
361
- }
362
- };
360
+ },
361
+ autobind: function () {} };
363
362
 
363
+ // noop
364
364
  function validateTypeDef(Constructor, typeDef, location) {
365
365
  for (var propName in typeDef) {
366
366
  if (typeDef.hasOwnProperty(propName)) {
@@ -429,7 +429,7 @@ function mixSpecIntoComponent(Constructor, spec) {
429
429
  var isReactClassMethod = ReactClassInterface.hasOwnProperty(name);
430
430
  var isAlreadyDefined = proto.hasOwnProperty(name);
431
431
  var isFunction = typeof property === 'function';
432
- var shouldAutoBind = isFunction && !isReactClassMethod && !isAlreadyDefined;
432
+ var shouldAutoBind = isFunction && !isReactClassMethod && !isAlreadyDefined && spec.autobind !== false;
433
433
 
434
434
  if (shouldAutoBind) {
435
435
  if (!proto.__reactAutoBindMap) {
@@ -593,7 +593,7 @@ function bindAutoBindMethods(component) {
593
593
  for (var autoBindKey in component.__reactAutoBindMap) {
594
594
  if (component.__reactAutoBindMap.hasOwnProperty(autoBindKey)) {
595
595
  var method = component.__reactAutoBindMap[autoBindKey];
596
- component[autoBindKey] = bindAutoBindMethod(component, ReactErrorUtils.guard(method, component.constructor.displayName + '.' + autoBindKey));
596
+ component[autoBindKey] = bindAutoBindMethod(component, method);
597
597
  }
598
598
  }
599
599
  }
@@ -13,6 +13,7 @@
13
13
 
14
14
  var ReactNoopUpdateQueue = require('./ReactNoopUpdateQueue');
15
15
 
16
+ var canDefineProperty = require('./canDefineProperty');
16
17
  var emptyObject = require('fbjs/lib/emptyObject');
17
18
  var invariant = require('fbjs/lib/invariant');
18
19
  var warning = require('fbjs/lib/warning');
@@ -29,6 +30,8 @@ function ReactComponent(props, context, updater) {
29
30
  this.updater = updater || ReactNoopUpdateQueue;
30
31
  }
31
32
 
33
+ ReactComponent.prototype.isReactComponent = {};
34
+
32
35
  /**
33
36
  * Sets a subset of the state. Always use this to mutate
34
37
  * state. You should treat `this.state` as immutable.
@@ -93,22 +96,20 @@ ReactComponent.prototype.forceUpdate = function (callback) {
93
96
  */
94
97
  if (process.env.NODE_ENV !== 'production') {
95
98
  var deprecatedAPIs = {
96
- getDOMNode: ['getDOMNode', 'Use React.findDOMNode(component) instead.'],
99
+ getDOMNode: ['getDOMNode', 'Use ReactDOM.findDOMNode(component) instead.'],
97
100
  isMounted: ['isMounted', 'Instead, make sure to clean up subscriptions and pending requests in ' + 'componentWillUnmount to prevent memory leaks.'],
98
- replaceProps: ['replaceProps', 'Instead, call React.render again at the top level.'],
101
+ replaceProps: ['replaceProps', 'Instead, call render again at the top level.'],
99
102
  replaceState: ['replaceState', 'Refactor your code to use setState instead (see ' + 'https://github.com/facebook/react/issues/3236).'],
100
- setProps: ['setProps', 'Instead, call React.render again at the top level.']
103
+ setProps: ['setProps', 'Instead, call render again at the top level.']
101
104
  };
102
105
  var defineDeprecationWarning = function (methodName, info) {
103
- try {
106
+ if (canDefineProperty) {
104
107
  Object.defineProperty(ReactComponent.prototype, methodName, {
105
108
  get: function () {
106
109
  process.env.NODE_ENV !== 'production' ? warning(false, '%s(...) is deprecated in plain JavaScript React classes. %s', info[0], info[1]) : undefined;
107
110
  return undefined;
108
111
  }
109
112
  });
110
- } catch (x) {
111
- // IE will fail on defineProperty (es5-shim/sham too)
112
113
  }
113
114
  };
114
115
  for (var fnName in deprecatedAPIs) {
@@ -38,6 +38,12 @@ function getDeclarationErrorAddendum(component) {
38
38
  return '';
39
39
  }
40
40
 
41
+ function StatelessComponent(Component) {}
42
+ StatelessComponent.prototype.render = function () {
43
+ var Component = ReactInstanceMap.get(this)._currentElement.type;
44
+ return Component(this.props, this.context, this.updater);
45
+ };
46
+
41
47
  /**
42
48
  * ------------------ The Life-Cycle of a Composite Component ------------------
43
49
  *
@@ -126,12 +132,43 @@ var ReactCompositeComponentMixin = {
126
132
  var Component = this._currentElement.type;
127
133
 
128
134
  // Initialize the public class
129
- var inst = new Component(publicProps, publicContext, ReactUpdateQueue);
135
+ var inst;
136
+ var renderedElement;
137
+
138
+ // This is a way to detect if Component is a stateless arrow function
139
+ // component, which is not newable. It might not be 100% reliable but is
140
+ // something we can do until we start detecting that Component extends
141
+ // React.Component. We already assume that typeof Component === 'function'.
142
+ var canInstantiate = ('prototype' in Component);
143
+
144
+ if (canInstantiate) {
145
+ if (process.env.NODE_ENV !== 'production') {
146
+ ReactCurrentOwner.current = this;
147
+ try {
148
+ inst = new Component(publicProps, publicContext, ReactUpdateQueue);
149
+ } finally {
150
+ ReactCurrentOwner.current = null;
151
+ }
152
+ } else {
153
+ inst = new Component(publicProps, publicContext, ReactUpdateQueue);
154
+ }
155
+ }
156
+
157
+ if (!canInstantiate || inst === null || inst === false || ReactElement.isValidElement(inst)) {
158
+ renderedElement = inst;
159
+ inst = new StatelessComponent(Component);
160
+ }
130
161
 
131
162
  if (process.env.NODE_ENV !== 'production') {
132
163
  // This will throw later in _renderValidatedComponent, but add an early
133
164
  // warning now to help debugging
134
- process.env.NODE_ENV !== 'production' ? warning(inst.render != null, '%s(...): No `render` method found on the returned component ' + 'instance: you may have forgotten to define `render` in your ' + 'component or you may have accidentally tried to render an element ' + 'whose type is a function that isn\'t a React component.', Component.displayName || Component.name || 'Component') : undefined;
165
+ if (inst.render == null) {
166
+ process.env.NODE_ENV !== 'production' ? warning(false, '%s(...): No `render` method found on the returned component ' + 'instance: you may have forgotten to define `render`, returned ' + 'null/false from a stateless component, or tried to render an ' + 'element whose type is a function that isn\'t a React component.', Component.displayName || Component.name || 'Component') : undefined;
167
+ } else {
168
+ // We support ES6 inheriting from React.Component, the module pattern,
169
+ // and stateless components, but not ES6 classes that don't extend
170
+ process.env.NODE_ENV !== 'production' ? warning(Component.prototype && Component.prototype.isReactComponent || !canInstantiate || !(inst instanceof Component), '%s(...): React component classes must extend React.Component.', Component.displayName || Component.name || 'Component') : undefined;
171
+ }
135
172
  }
136
173
 
137
174
  // These should be set up in the constructor, but as a convenience for
@@ -178,7 +215,10 @@ var ReactCompositeComponentMixin = {
178
215
  }
179
216
  }
180
217
 
181
- var renderedElement = this._renderValidatedComponent();
218
+ // If not a stateless component, we now render
219
+ if (renderedElement === undefined) {
220
+ renderedElement = this._renderValidatedComponent();
221
+ }
182
222
 
183
223
  this._renderedComponent = this._instantiateReactComponent(renderedElement);
184
224
 
@@ -205,6 +245,7 @@ var ReactCompositeComponentMixin = {
205
245
 
206
246
  ReactReconciler.unmountComponent(this._renderedComponent);
207
247
  this._renderedComponent = null;
248
+ this._instance = null;
208
249
 
209
250
  // Reset pending fields
210
251
  // Even if this component is scheduled for another update in ReactUpdates,
@@ -340,7 +381,7 @@ var ReactCompositeComponentMixin = {
340
381
  }
341
382
  if (error instanceof Error) {
342
383
  // We may want to extend this logic for similar errors in
343
- // React.render calls, so I'm abstracting it away into
384
+ // top-level render calls, so I'm abstracting it away into
344
385
  // a function to minimize refactoring in the future
345
386
  var addendum = getDeclarationErrorAddendum(this);
346
387
 
@@ -583,8 +624,14 @@ var ReactCompositeComponentMixin = {
583
624
  */
584
625
  attachRef: function (ref, component) {
585
626
  var inst = this.getPublicInstance();
627
+ !(inst != null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Stateless function components cannot have refs.') : invariant(false) : undefined;
628
+ var publicComponentInstance = component.getPublicInstance();
629
+ if (process.env.NODE_ENV !== 'production') {
630
+ var componentName = component && component.getName ? component.getName() : 'a component';
631
+ process.env.NODE_ENV !== 'production' ? warning(publicComponentInstance != null, 'Stateless function components cannot be given refs ' + '(See ref "%s" in %s created by %s). ' + 'Attempts to access this ref will fail.', ref, componentName, this.getName()) : undefined;
632
+ }
586
633
  var refs = inst.refs === emptyObject ? inst.refs = {} : inst.refs;
587
- refs[ref] = component.getPublicInstance();
634
+ refs[ref] = publicComponentInstance;
588
635
  },
589
636
 
590
637
  /**
@@ -613,14 +660,18 @@ var ReactCompositeComponentMixin = {
613
660
 
614
661
  /**
615
662
  * Get the publicly accessible representation of this component - i.e. what
616
- * is exposed by refs and returned by React.render. Can be null for stateless
663
+ * is exposed by refs and returned by render. Can be null for stateless
617
664
  * components.
618
665
  *
619
666
  * @return {ReactComponent} the public component instance.
620
667
  * @internal
621
668
  */
622
669
  getPublicInstance: function () {
623
- return this._instance;
670
+ var inst = this._instance;
671
+ if (inst instanceof StatelessComponent) {
672
+ return null;
673
+ }
674
+ return inst;
624
675
  },
625
676
 
626
677
  // Stub
package/lib/ReactDOM.js CHANGED
@@ -21,6 +21,7 @@ var ReactMount = require('./ReactMount');
21
21
  var ReactPerf = require('./ReactPerf');
22
22
  var ReactReconciler = require('./ReactReconciler');
23
23
  var ReactUpdates = require('./ReactUpdates');
24
+ var ReactVersion = require('./ReactVersion');
24
25
 
25
26
  var findDOMNode = require('./findDOMNode');
26
27
  var renderSubtreeIntoContainer = require('./renderSubtreeIntoContainer');
@@ -34,6 +35,7 @@ var React = {
34
35
  findDOMNode: findDOMNode,
35
36
  render: render,
36
37
  unmountComponentAtNode: ReactMount.unmountComponentAtNode,
38
+ version: ReactVersion,
37
39
 
38
40
  /* eslint-disable camelcase */
39
41
  unstable_batchedUpdates: ReactUpdates.batchedUpdates,
@@ -57,15 +59,15 @@ if (process.env.NODE_ENV !== 'production') {
57
59
  var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
58
60
  if (ExecutionEnvironment.canUseDOM && window.top === window.self) {
59
61
 
60
- // If we're in Chrome, look for the devtools marker and provide a download
61
- // link if not installed.
62
- if (navigator.userAgent.indexOf('Chrome') > -1) {
63
- if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') {
62
+ // First check if devtools is not installed
63
+ if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') {
64
+ // If we're in Chrome or Firefox, provide a download link if not installed.
65
+ if (navigator.userAgent.indexOf('Chrome') > -1 && navigator.userAgent.indexOf('Edge') === -1 || navigator.userAgent.indexOf('Firefox') > -1) {
64
66
  console.debug('Download the React DevTools for a better development experience: ' + 'https://fb.me/react-devtools');
65
67
  }
66
68
  }
67
69
 
68
- // If we're in IE8, check to see if we are in combatibility mode and provide
70
+ // If we're in IE8, check to see if we are in compatibility mode and provide
69
71
  // information on preventing compatibility mode
70
72
  var ieCompatibilityMode = document.documentMode && document.documentMode < 8;
71
73
 
@@ -32,10 +32,13 @@ var ReactPerf = require('./ReactPerf');
32
32
  var ReactUpdateQueue = require('./ReactUpdateQueue');
33
33
 
34
34
  var assign = require('./Object.assign');
35
+ var canDefineProperty = require('./canDefineProperty');
35
36
  var escapeTextContentForBrowser = require('./escapeTextContentForBrowser');
36
37
  var invariant = require('fbjs/lib/invariant');
37
38
  var isEventSupported = require('./isEventSupported');
38
39
  var keyOf = require('fbjs/lib/keyOf');
40
+ var setInnerHTML = require('./setInnerHTML');
41
+ var setTextContent = require('./setTextContent');
39
42
  var shallowEqual = require('fbjs/lib/shallowEqual');
40
43
  var validateDOMNesting = require('./validateDOMNesting');
41
44
  var warning = require('fbjs/lib/warning');
@@ -48,15 +51,10 @@ var registrationNameModules = ReactBrowserEventEmitter.registrationNameModules;
48
51
  var CONTENT_TYPES = { 'string': true, 'number': true };
49
52
 
50
53
  var STYLE = keyOf({ style: null });
54
+ var HTML = keyOf({ __html: null });
51
55
 
52
56
  var ELEMENT_NODE_TYPE = 1;
53
57
 
54
- var canDefineProperty = false;
55
- try {
56
- Object.defineProperty({}, 'test', { get: function () {} });
57
- canDefineProperty = true;
58
- } catch (e) {}
59
-
60
58
  function getDeclarationErrorAddendum(internalInstance) {
61
59
  if (internalInstance) {
62
60
  var owner = internalInstance._currentElement._owner || null;
@@ -110,7 +108,7 @@ function legacySetStateEtc() {
110
108
  function legacySetProps(partialProps, callback) {
111
109
  var component = this._reactInternalComponent;
112
110
  if (process.env.NODE_ENV !== 'production') {
113
- process.env.NODE_ENV !== 'production' ? warning(false, 'ReactDOMComponent: Do not access .setProps() of a DOM node. ' + 'Instead, call React.render again at the top level.%s', getDeclarationErrorAddendum(component)) : undefined;
111
+ process.env.NODE_ENV !== 'production' ? warning(false, 'ReactDOMComponent: Do not access .setProps() of a DOM node. ' + 'Instead, call ReactDOM.render again at the top level.%s', getDeclarationErrorAddendum(component)) : undefined;
114
112
  }
115
113
  if (!component) {
116
114
  return;
@@ -124,7 +122,7 @@ function legacySetProps(partialProps, callback) {
124
122
  function legacyReplaceProps(partialProps, callback) {
125
123
  var component = this._reactInternalComponent;
126
124
  if (process.env.NODE_ENV !== 'production') {
127
- process.env.NODE_ENV !== 'production' ? warning(false, 'ReactDOMComponent: Do not access .replaceProps() of a DOM node. ' + 'Instead, call React.render again at the top level.%s', getDeclarationErrorAddendum(component)) : undefined;
125
+ process.env.NODE_ENV !== 'production' ? warning(false, 'ReactDOMComponent: Do not access .replaceProps() of a DOM node. ' + 'Instead, call ReactDOM.render again at the top level.%s', getDeclarationErrorAddendum(component)) : undefined;
128
126
  }
129
127
  if (!component) {
130
128
  return;
@@ -135,6 +133,30 @@ function legacyReplaceProps(partialProps, callback) {
135
133
  }
136
134
  }
137
135
 
136
+ function friendlyStringify(obj) {
137
+ if (typeof obj === 'object') {
138
+ if (Array.isArray(obj)) {
139
+ return '[' + obj.map(friendlyStringify).join(', ') + ']';
140
+ } else {
141
+ var pairs = [];
142
+ for (var key in obj) {
143
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
144
+ var keyEscaped = /^[a-z$_][\w$_]*$/i.test(key) ? key : JSON.stringify(key);
145
+ pairs.push(keyEscaped + ': ' + friendlyStringify(obj[key]));
146
+ }
147
+ }
148
+ return '{' + pairs.join(', ') + '}';
149
+ }
150
+ } else if (typeof obj === 'string') {
151
+ return JSON.stringify(obj);
152
+ } else if (typeof obj === 'function') {
153
+ return '[function object]';
154
+ }
155
+ // Differs from JSON.stringify in that undefined becauses undefined and that
156
+ // inf and nan don't become null
157
+ return String(obj);
158
+ }
159
+
138
160
  var styleMutationWarning = {};
139
161
 
140
162
  function checkAndWarnForMutatedStyle(style1, style2, component) {
@@ -160,14 +182,9 @@ function checkAndWarnForMutatedStyle(style1, style2, component) {
160
182
 
161
183
  styleMutationWarning[hash] = true;
162
184
 
163
- process.env.NODE_ENV !== 'production' ? warning(false, '`%s` was passed a style object that has previously been mutated. ' + 'Mutating `style` is deprecated. Consider cloning it beforehand. Check ' + 'the `render` %s. Previous style: %s. Mutated style: %s.', componentName, owner ? 'of `' + ownerName + '`' : 'using <' + componentName + '>', JSON.stringify(style1), JSON.stringify(style2)) : undefined;
185
+ process.env.NODE_ENV !== 'production' ? warning(false, '`%s` was passed a style object that has previously been mutated. ' + 'Mutating `style` is deprecated. Consider cloning it beforehand. Check ' + 'the `render` %s. Previous style: %s. Mutated style: %s.', componentName, owner ? 'of `' + ownerName + '`' : 'using <' + componentName + '>', friendlyStringify(style1), friendlyStringify(style2)) : undefined;
164
186
  }
165
187
 
166
- /**
167
- * Optionally injectable operations for mutating the DOM
168
- */
169
- var BackendIDOperations = null;
170
-
171
188
  /**
172
189
  * @param {object} component
173
190
  * @param {?object} props
@@ -184,13 +201,13 @@ function assertValidProps(component, props) {
184
201
  }
185
202
  if (props.dangerouslySetInnerHTML != null) {
186
203
  !(props.children == null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.') : invariant(false) : undefined;
187
- !(typeof props.dangerouslySetInnerHTML === 'object' && '__html' in props.dangerouslySetInnerHTML) ? process.env.NODE_ENV !== 'production' ? invariant(false, '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' + 'Please visit https://fb.me/react-invariant-dangerously-set-inner-html ' + 'for more information.') : invariant(false) : undefined;
204
+ !(typeof props.dangerouslySetInnerHTML === 'object' && HTML in props.dangerouslySetInnerHTML) ? process.env.NODE_ENV !== 'production' ? invariant(false, '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' + 'Please visit https://fb.me/react-invariant-dangerously-set-inner-html ' + 'for more information.') : invariant(false) : undefined;
188
205
  }
189
206
  if (process.env.NODE_ENV !== 'production') {
190
207
  process.env.NODE_ENV !== 'production' ? warning(props.innerHTML == null, 'Directly setting property `innerHTML` is not permitted. ' + 'For more information, lookup documentation on `dangerouslySetInnerHTML`.') : undefined;
191
208
  process.env.NODE_ENV !== 'production' ? warning(!props.contentEditable || props.children == null, 'A component is `contentEditable` and contains `children` managed by ' + 'React. It is now your responsibility to guarantee that none of ' + 'those nodes are unexpectedly modified or duplicated. This is ' + 'probably not intentional.') : undefined;
192
209
  }
193
- !(props.style == null || typeof props.style === 'object') ? process.env.NODE_ENV !== 'production' ? invariant(false, 'The `style` prop expects a mapping from style properties to values, ' + 'not a string. For example, style={{marginRight: spacing + \'em\'}} when ' + 'using JSX.') : invariant(false) : undefined;
210
+ !(props.style == null || typeof props.style === 'object') ? process.env.NODE_ENV !== 'production' ? invariant(false, 'The `style` prop expects a mapping from style properties to values, ' + 'not a string. For example, style={{marginRight: spacing + \'em\'}} when ' + 'using JSX.%s', getDeclarationErrorAddendum(component)) : invariant(false) : undefined;
194
211
  }
195
212
 
196
213
  function enqueuePutListener(id, registrationName, listener, transaction) {
@@ -224,12 +241,12 @@ var mediaEvents = {
224
241
  topCanPlayThrough: 'canplaythrough',
225
242
  topDurationChange: 'durationchange',
226
243
  topEmptied: 'emptied',
244
+ topEncrypted: 'encrypted',
227
245
  topEnded: 'ended',
228
246
  topError: 'error',
229
247
  topLoadedData: 'loadeddata',
230
248
  topLoadedMetadata: 'loadedmetadata',
231
249
  topLoadStart: 'loadstart',
232
- topOnEncrypted: 'onencrypted',
233
250
  topPause: 'pause',
234
251
  topPlay: 'play',
235
252
  topPlaying: 'playing',
@@ -277,6 +294,10 @@ function trapBubbledEventsLocal() {
277
294
  }
278
295
  }
279
296
 
297
+ function mountReadyInputWrapper() {
298
+ ReactDOMInput.mountReadyWrapper(this);
299
+ }
300
+
280
301
  function postUpdateSelectWrapper() {
281
302
  ReactDOMSelect.postUpdateWrapper(this);
282
303
  }
@@ -331,13 +352,11 @@ function validateDangerousTag(tag) {
331
352
  }
332
353
  }
333
354
 
334
- function processChildContext(context, inst) {
335
- if (process.env.NODE_ENV !== 'production') {
336
- // Pass down our tag name to child components for validation purposes
337
- context = assign({}, context);
338
- var info = context[validateDOMNesting.ancestorInfoContextKey];
339
- context[validateDOMNesting.ancestorInfoContextKey] = validateDOMNesting.updatedAncestorInfo(info, inst._tag, inst);
340
- }
355
+ function processChildContextDev(context, inst) {
356
+ // Pass down our tag name to child components for validation purposes
357
+ context = assign({}, context);
358
+ var info = context[validateDOMNesting.ancestorInfoContextKey];
359
+ context[validateDOMNesting.ancestorInfoContextKey] = validateDOMNesting.updatedAncestorInfo(info, inst._tag, inst);
341
360
  return context;
342
361
  }
343
362
 
@@ -369,6 +388,10 @@ function ReactDOMComponent(tag) {
369
388
  this._wrapperState = null;
370
389
  this._topLevelWrapper = null;
371
390
  this._nodeWithLegacyProperties = null;
391
+ if (process.env.NODE_ENV !== 'production') {
392
+ this._unprocessedContextDev = null;
393
+ this._processedContextDev = null;
394
+ }
372
395
  }
373
396
 
374
397
  ReactDOMComponent.displayName = 'ReactDOMComponent';
@@ -434,12 +457,37 @@ ReactDOMComponent.Mixin = {
434
457
  }
435
458
  }
436
459
 
437
- var tagOpen = this._createOpenTagMarkupAndPutListeners(transaction, props);
438
- var tagContent = this._createContentMarkup(transaction, props, context);
460
+ if (process.env.NODE_ENV !== 'production') {
461
+ this._unprocessedContextDev = context;
462
+ this._processedContextDev = processChildContextDev(context, this);
463
+ context = this._processedContextDev;
464
+ }
465
+
466
+ var mountImage;
467
+ if (transaction.useCreateElement) {
468
+ var ownerDocument = context[ReactMount.ownerDocumentContextKey];
469
+ var el = ownerDocument.createElement(this._currentElement.type);
470
+ DOMPropertyOperations.setAttributeForID(el, this._rootNodeID);
471
+ // Populate node cache
472
+ ReactMount.getID(el);
473
+ this._updateDOMProperties({}, props, transaction, el);
474
+ this._createInitialChildren(transaction, props, context, el);
475
+ mountImage = el;
476
+ } else {
477
+ var tagOpen = this._createOpenTagMarkupAndPutListeners(transaction, props);
478
+ var tagContent = this._createContentMarkup(transaction, props, context);
479
+ if (!tagContent && omittedCloseTags[this._tag]) {
480
+ mountImage = tagOpen + '/>';
481
+ } else {
482
+ mountImage = tagOpen + '>' + tagContent + '</' + this._currentElement.type + '>';
483
+ }
484
+ }
439
485
 
440
486
  switch (this._tag) {
441
- case 'button':
442
487
  case 'input':
488
+ transaction.getReactMountReady().enqueue(mountReadyInputWrapper, this);
489
+ // falls through
490
+ case 'button':
443
491
  case 'select':
444
492
  case 'textarea':
445
493
  if (props.autoFocus) {
@@ -448,10 +496,7 @@ ReactDOMComponent.Mixin = {
448
496
  break;
449
497
  }
450
498
 
451
- if (!tagContent && omittedCloseTags[this._tag]) {
452
- return tagOpen + '/>';
453
- }
454
- return tagOpen + '>' + tagContent + '</' + this._currentElement.type + '>';
499
+ return mountImage;
455
500
  },
456
501
 
457
502
  /**
@@ -479,7 +524,9 @@ ReactDOMComponent.Mixin = {
479
524
  continue;
480
525
  }
481
526
  if (registrationNameModules.hasOwnProperty(propKey)) {
482
- enqueuePutListener(this._rootNodeID, propKey, propValue, transaction);
527
+ if (propValue) {
528
+ enqueuePutListener(this._rootNodeID, propKey, propValue, transaction);
529
+ }
483
530
  } else {
484
531
  if (propKey === STYLE) {
485
532
  if (propValue) {
@@ -538,7 +585,7 @@ ReactDOMComponent.Mixin = {
538
585
  // TODO: Validate that text is allowed as a child of this node
539
586
  ret = escapeTextContentForBrowser(contentToUse);
540
587
  } else if (childrenToUse != null) {
541
- var mountImages = this.mountChildren(childrenToUse, transaction, processChildContext(context, this));
588
+ var mountImages = this.mountChildren(childrenToUse, transaction, context);
542
589
  ret = mountImages.join('');
543
590
  }
544
591
  }
@@ -559,6 +606,28 @@ ReactDOMComponent.Mixin = {
559
606
  }
560
607
  },
561
608
 
609
+ _createInitialChildren: function (transaction, props, context, el) {
610
+ // Intentional use of != to avoid catching zero/false.
611
+ var innerHTML = props.dangerouslySetInnerHTML;
612
+ if (innerHTML != null) {
613
+ if (innerHTML.__html != null) {
614
+ setInnerHTML(el, innerHTML.__html);
615
+ }
616
+ } else {
617
+ var contentToUse = CONTENT_TYPES[typeof props.children] ? props.children : null;
618
+ var childrenToUse = contentToUse != null ? null : props.children;
619
+ if (contentToUse != null) {
620
+ // TODO: Validate that text is allowed as a child of this node
621
+ setTextContent(el, contentToUse);
622
+ } else if (childrenToUse != null) {
623
+ var mountImages = this.mountChildren(childrenToUse, transaction, context);
624
+ for (var i = 0; i < mountImages.length; i++) {
625
+ el.appendChild(mountImages[i]);
626
+ }
627
+ }
628
+ }
629
+ },
630
+
562
631
  /**
563
632
  * Receives a next element and updates the component.
564
633
  *
@@ -612,9 +681,20 @@ ReactDOMComponent.Mixin = {
612
681
  break;
613
682
  }
614
683
 
684
+ if (process.env.NODE_ENV !== 'production') {
685
+ // If the context is reference-equal to the old one, pass down the same
686
+ // processed object so the update bailout in ReactReconciler behaves
687
+ // correctly (and identically in dev and prod). See #5005.
688
+ if (this._unprocessedContextDev !== context) {
689
+ this._unprocessedContextDev = context;
690
+ this._processedContextDev = processChildContextDev(context, this);
691
+ }
692
+ context = this._processedContextDev;
693
+ }
694
+
615
695
  assertValidProps(this, nextProps);
616
- this._updateDOMProperties(lastProps, nextProps, transaction);
617
- this._updateDOMChildren(lastProps, nextProps, transaction, processChildContext(context, this));
696
+ this._updateDOMProperties(lastProps, nextProps, transaction, null);
697
+ this._updateDOMChildren(lastProps, nextProps, transaction, context);
618
698
 
619
699
  if (!canDefineProperty && this._nodeWithLegacyProperties) {
620
700
  this._nodeWithLegacyProperties.props = nextProps;
@@ -642,8 +722,9 @@ ReactDOMComponent.Mixin = {
642
722
  * @param {object} lastProps
643
723
  * @param {object} nextProps
644
724
  * @param {ReactReconcileTransaction} transaction
725
+ * @param {?DOMElement} node
645
726
  */
646
- _updateDOMProperties: function (lastProps, nextProps, transaction) {
727
+ _updateDOMProperties: function (lastProps, nextProps, transaction, node) {
647
728
  var propKey;
648
729
  var styleName;
649
730
  var styleUpdates;
@@ -668,7 +749,10 @@ ReactDOMComponent.Mixin = {
668
749
  deleteListener(this._rootNodeID, propKey);
669
750
  }
670
751
  } else if (DOMProperty.properties[propKey] || DOMProperty.isCustomAttribute(propKey)) {
671
- BackendIDOperations.deletePropertyByID(this._rootNodeID, propKey);
752
+ if (!node) {
753
+ node = ReactMount.getNode(this._rootNodeID);
754
+ }
755
+ DOMPropertyOperations.deleteValueForProperty(node, propKey);
672
756
  }
673
757
  }
674
758
  for (propKey in nextProps) {
@@ -713,13 +797,29 @@ ReactDOMComponent.Mixin = {
713
797
  deleteListener(this._rootNodeID, propKey);
714
798
  }
715
799
  } else if (isCustomComponent(this._tag, nextProps)) {
716
- BackendIDOperations.updateAttributeByID(this._rootNodeID, propKey, nextProp);
800
+ if (!node) {
801
+ node = ReactMount.getNode(this._rootNodeID);
802
+ }
803
+ DOMPropertyOperations.setValueForAttribute(node, propKey, nextProp);
717
804
  } else if (DOMProperty.properties[propKey] || DOMProperty.isCustomAttribute(propKey)) {
718
- BackendIDOperations.updatePropertyByID(this._rootNodeID, propKey, nextProp);
805
+ if (!node) {
806
+ node = ReactMount.getNode(this._rootNodeID);
807
+ }
808
+ // If we're updating to null or undefined, we should remove the property
809
+ // from the DOM node instead of inadvertantly setting to a string. This
810
+ // brings us in line with the same behavior we have on initial render.
811
+ if (nextProp != null) {
812
+ DOMPropertyOperations.setValueForProperty(node, propKey, nextProp);
813
+ } else {
814
+ DOMPropertyOperations.deleteValueForProperty(node, propKey);
815
+ }
719
816
  }
720
817
  }
721
818
  if (styleUpdates) {
722
- BackendIDOperations.updateStylesByID(this._rootNodeID, styleUpdates);
819
+ if (!node) {
820
+ node = ReactMount.getNode(this._rootNodeID);
821
+ }
822
+ CSSPropertyOperations.setValueForStyles(node, styleUpdates);
723
823
  }
724
824
  },
725
825
 
@@ -853,10 +953,4 @@ ReactPerf.measureMethods(ReactDOMComponent, 'ReactDOMComponent', {
853
953
 
854
954
  assign(ReactDOMComponent.prototype, ReactDOMComponent.Mixin, ReactMultiChild.Mixin);
855
955
 
856
- ReactDOMComponent.injection = {
857
- injectIDOperations: function (IDOperations) {
858
- ReactDOMComponent.BackendIDOperations = BackendIDOperations = IDOperations;
859
- }
860
- };
861
-
862
956
  module.exports = ReactDOMComponent;