Don't pass the Fiber around instead use the DOM node
To avoid exposing the implementation details of fibers we'll just pass the DOM node around instead. We'll attach any additional wrapper state on it. We don't have to do it that way. We can also just invert the relationship and put the node in the wrapper state. I'll probably just get rid of the wrapper object and just put them as expandos on the DOM.
Sebastian Markbage committed
Nov 17, 2016 at 20:10 UTC
4b017507e2a4edf4c35ecd001becfc65853df4ab
5 files changed
+184
-180
src/renderers/dom/fiber/ReactDOMFiberComponent.js
+105
-116
@@ -14,8 +14,6 @@
14
15
'use strict';
16
17
-import type { Fiber } from 'ReactFiber';
18
-
17
var CSSPropertyOperations = require('CSSPropertyOperations');
18
var DOMNamespaces = require('DOMNamespaces');
19
var DOMProperty = require('DOMProperty');
@@ -39,7 +37,6 @@ var inputValueTracking = require('inputValueTracking');
37
var warning = require('warning');
38
var didWarnShadyDOM = false;
39
42
-var getNode = ReactDOMComponentTree.getNodeFromInstance;
40
var listenTo = ReactBrowserEventEmitter.listenTo;
41
var registrationNameModules = EventPluginRegistry.registrationNameModules;
42
@@ -162,7 +159,7 @@ var mediaEvents = {
159
topWaiting: 'waiting',
160
};
161
165
-function trapClickOnNonInteractiveElement(inst) {
162
+function trapClickOnNonInteractiveElement(node : any) {
163
// Mobile Safari does not fire properly bubble click events on
164
// non-interactive elements, which means delegated click listeners do not
165
// fire. The workaround for this bug involves attaching an empty click
@@ -172,20 +169,16 @@ function trapClickOnNonInteractiveElement(inst) {
169
// bookkeeping for it. Not sure if we need to clear it when the listener is
170
// removed.
171
// TODO: Only do this for the relevant Safaris maybe?
175
- var node = getNode(inst);
172
node.onclick = emptyFunction;
173
}
174
179
-function trapBubbledEventsLocal(inst, tag) {
175
+function trapBubbledEventsLocal(node : Element, tag : string) {
176
// If a component renders to null or if another component fatals and causes
177
// the state of the tree to be corrupted, `node` here can be null.
182
- var node = getNode(inst);
183
- invariant(
184
- node,
185
- 'trapBubbledEvent(...): Requires node to be rendered.'
186
- );
178
179
// TODO: Make sure that we check isMounted before firing any of these events.
180
+ // TODO: Inline these below since we're calling this from an equivalent
181
+ // switch statement.
182
switch (tag) {
183
case 'iframe':
184
case 'object':
@@ -307,12 +300,12 @@ function isCustomComponent(tagName, props) {
300
*
301
* TODO: Benchmark whether checking for changed values in memory actually
302
* improves performance (especially statically positioned elements).
310
- * TODO: Benchmark the effects of putting workInProgress at the top since 99% of props
303
+ * TODO: Benchmark the effects of putting this at the top since 99% of props
304
* do not change for a given reconciliation.
305
* TODO: Benchmark areas that can be improved with caching.
306
*/
307
function updateDOMProperties(
315
- workInProgress : Fiber,
308
+ domElement : Element,
309
rootContainerElement : Element,
310
lastProps : null | Object,
311
nextProps : Object,
@@ -347,13 +340,13 @@ function updateDOMProperties(
340
// Do nothing for deleted listeners.
341
} else if (wasCustomComponentTag) {
342
DOMPropertyOperations.deleteValueForAttribute(
350
- getNode(workInProgress),
343
+ domElement,
344
propKey
345
);
346
} else if (
347
DOMProperty.properties[propKey] ||
348
DOMProperty.isCustomAttribute(propKey)) {
356
- DOMPropertyOperations.deleteValueForProperty(getNode(workInProgress), propKey);
349
+ DOMPropertyOperations.deleteValueForProperty(domElement, propKey);
350
}
351
}
352
for (propKey in nextProps) {
@@ -400,10 +393,10 @@ function updateDOMProperties(
393
if (nextHtml) {
394
if (lastHtml) {
395
if (lastHtml !== nextHtml) {
403
- setInnerHTML(getNode(workInProgress), '' + nextHtml);
396
+ setInnerHTML(domElement, '' + nextHtml);
397
}
398
} else {
406
- setInnerHTML(getNode(workInProgress), nextHtml);
399
+ setInnerHTML(domElement, nextHtml);
400
}
401
} else {
402
// TODO: It might be too late to clear this if we have children
@@ -411,9 +404,9 @@ function updateDOMProperties(
404
}
405
} else if (propKey === CHILDREN) {
406
if (typeof nextProp === 'string') {
414
- setTextContent(getNode(workInProgress), nextProp);
407
+ setTextContent(domElement, nextProp);
408
} else if (typeof nextProp === 'number') {
416
- setTextContent(getNode(workInProgress), '' + nextProp);
409
+ setTextContent(domElement, '' + nextProp);
410
}
411
} else if (propKey === SUPPRESS_CONTENT_EDITABLE_WARNING) {
412
// Noop
@@ -423,29 +416,28 @@ function updateDOMProperties(
416
}
417
} else if (isCustomComponentTag) {
418
DOMPropertyOperations.setValueForAttribute(
426
- getNode(workInProgress),
419
+ domElement,
420
propKey,
421
nextProp
422
);
423
} else if (
424
DOMProperty.properties[propKey] ||
425
DOMProperty.isCustomAttribute(propKey)) {
433
- var node = getNode(workInProgress);
426
// If we're updating to null or undefined, we should remove the property
427
// from the DOM node instead of inadvertently setting to a string. This
428
// brings us in line with the same behavior we have on initial render.
429
if (nextProp != null) {
438
- DOMPropertyOperations.setValueForProperty(node, propKey, nextProp);
430
+ DOMPropertyOperations.setValueForProperty(domElement, propKey, nextProp);
431
} else {
440
- DOMPropertyOperations.deleteValueForProperty(node, propKey);
432
+ DOMPropertyOperations.deleteValueForProperty(domElement, propKey);
433
}
434
}
435
}
436
if (styleUpdates) {
437
CSSPropertyOperations.setValueForStyles(
446
- getNode(workInProgress),
438
+ domElement,
439
styleUpdates,
448
- workInProgress
440
+ null // TODO: Change CSSPropertyOperations to use getCurrentOwnerName.
441
);
442
}
443
}
@@ -459,7 +451,7 @@ var ReactDOMFiberComponent = {
451
},
452
453
mountComponent: function(
462
- workInProgress : Fiber,
454
+ internalInstanceHandle : Object,
455
tag : string,
456
rootContainerElement : Element,
457
props : Object,
@@ -469,53 +461,6 @@ var ReactDOMFiberComponent = {
461
// validateDangerousTag(tag);
462
// tag.toLowerCase(); Do we need to apply lower case only on non-custom elements?
463
472
- switch (tag) {
473
- case 'audio':
474
- case 'form':
475
- case 'iframe':
476
- case 'img':
477
- case 'link':
478
- case 'object':
479
- case 'source':
480
- case 'video':
481
- trapBubbledEventsLocal(workInProgress);
482
- break;
483
- case 'input':
484
- ReactDOMFiberInput.mountWrapper(workInProgress, props);
485
- props = ReactDOMFiberInput.getHostProps(workInProgress, props);
486
- // TODO: Make sure we check if this is still unmounted or do any clean
487
- // up necessary since we never stop tracking anymore.
488
- inputValueTracking.track(workInProgress);
489
- trapBubbledEventsLocal(workInProgress);
490
- // For controlled components we always need to ensure we're listening
491
- // to onChange. Even if there is no listener.
492
- ensureListeningTo(rootContainerElement, 'onChange');
493
- break;
494
- case 'option':
495
- ReactDOMFiberOption.mountWrapper(workInProgress, props);
496
- props = ReactDOMFiberOption.getHostProps(workInProgress, props);
497
- break;
498
- case 'select':
499
- ReactDOMFiberSelect.mountWrapper(workInProgress, props);
500
- props = ReactDOMFiberSelect.getHostProps(workInProgress, props);
501
- trapBubbledEventsLocal(workInProgress);
502
- // For controlled components we always need to ensure we're listening
503
- // to onChange. Even if there is no listener.
504
- ensureListeningTo(rootContainerElement, 'onChange');
505
- break;
506
- case 'textarea':
507
- ReactDOMFiberTextarea.mountWrapper(workInProgress, props);
508
- props = ReactDOMFiberTextarea.getHostProps(workInProgress, props);
509
- inputValueTracking.track(workInProgress);
510
- trapBubbledEventsLocal(workInProgress);
511
- // For controlled components we always need to ensure we're listening
512
- // to onChange. Even if there is no listener.
513
- ensureListeningTo(rootContainerElement, 'onChange');
514
- break;
515
- }
516
-
517
- assertValidProps(tag, props);
518
-
464
// We create tags in the namespace of their parent container, except HTML
465
// tags get no namespace.
466
var namespaceURI = rootContainerElement.namespaceURI;
@@ -534,7 +479,7 @@ var ReactDOMFiberComponent = {
479
}
480
481
var ownerDocument = rootContainerElement.ownerDocument;
537
- var el;
482
+ var domElement : Element;
483
if (namespaceURI === DOMNamespaces.html) {
484
if (tag === 'script') {
485
// Create the script via .innerHTML so its "parser-inserted" flag is
@@ -543,24 +488,24 @@ var ReactDOMFiberComponent = {
488
div.innerHTML = '<script></script>';
489
// This is guaranteed to yield a script element.
490
var firstChild = ((div.firstChild : any) : Element);
546
- el = div.removeChild(firstChild);
491
+ domElement = div.removeChild(firstChild);
492
} else if (props.is) {
548
- el = ownerDocument.createElement(tag, props.is);
493
+ domElement = ownerDocument.createElement(tag, props.is);
494
} else {
495
// Separate else branch instead of using `props.is || undefined` above becuase of a Firefox bug.
496
// See discussion in https://github.com/facebook/react/pull/6896
497
// and discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1276240
553
- el = ownerDocument.createElement(tag);
498
+ domElement = ownerDocument.createElement(tag);
499
}
500
} else {
556
- el = ownerDocument.createElementNS(
501
+ domElement = ownerDocument.createElementNS(
502
namespaceURI,
503
tag
504
);
505
}
506
var isCustomComponentTag = isCustomComponent(tag, props);
507
if (__DEV__) {
563
- if (isCustomComponentTag && !didWarnShadyDOM && el.shadyRoot) {
508
+ if (isCustomComponentTag && !didWarnShadyDOM && domElement.shadyRoot) {
509
warning(
510
false,
511
'%s is using shady DOM. Using shady DOM with React can ' +
@@ -570,9 +515,57 @@ var ReactDOMFiberComponent = {
515
didWarnShadyDOM = true;
516
}
517
}
573
- ReactDOMComponentTree.precacheFiberNode(workInProgress, el);
518
+
519
+ switch (tag) {
520
+ case 'audio':
521
+ case 'form':
522
+ case 'iframe':
523
+ case 'img':
524
+ case 'link':
525
+ case 'object':
526
+ case 'source':
527
+ case 'video':
528
+ trapBubbledEventsLocal(domElement, tag);
529
+ break;
530
+ case 'input':
531
+ ReactDOMFiberInput.mountWrapper(domElement, props);
532
+ props = ReactDOMFiberInput.getHostProps(domElement, props);
533
+ // TODO: Make sure we check if this is still unmounted or do any clean
534
+ // up necessary since we never stop tracking anymore.
535
+ inputValueTracking.track(domElement);
536
+ trapBubbledEventsLocal(domElement, tag);
537
+ // For controlled components we always need to ensure we're listening
538
+ // to onChange. Even if there is no listener.
539
+ ensureListeningTo(rootContainerElement, 'onChange');
540
+ break;
541
+ case 'option':
542
+ ReactDOMFiberOption.mountWrapper(domElement, props);
543
+ props = ReactDOMFiberOption.getHostProps(domElement, props);
544
+ break;
545
+ case 'select':
546
+ ReactDOMFiberSelect.mountWrapper(domElement, props);
547
+ props = ReactDOMFiberSelect.getHostProps(domElement, props);
548
+ trapBubbledEventsLocal(domElement, tag);
549
+ // For controlled components we always need to ensure we're listening
550
+ // to onChange. Even if there is no listener.
551
+ ensureListeningTo(rootContainerElement, 'onChange');
552
+ break;
553
+ case 'textarea':
554
+ ReactDOMFiberTextarea.mountWrapper(domElement, props);
555
+ props = ReactDOMFiberTextarea.getHostProps(domElement, props);
556
+ inputValueTracking.track(domElement); // TODO
557
+ trapBubbledEventsLocal(domElement, tag);
558
+ // For controlled components we always need to ensure we're listening
559
+ // to onChange. Even if there is no listener.
560
+ ensureListeningTo(rootContainerElement, 'onChange');
561
+ break;
562
+ }
563
+
564
+ assertValidProps(tag, props);
565
+
566
+ ReactDOMComponentTree.precacheFiberNode(internalInstanceHandle, domElement);
567
updateDOMProperties(
575
- workInProgress,
568
+ domElement,
569
rootContainerElement,
570
null,
571
props,
@@ -580,72 +573,73 @@ var ReactDOMFiberComponent = {
573
isCustomComponentTag
574
);
575
576
+ // TODO: All these autoFocus won't work because the component is not in the
577
+ // DOM yet. We need a special effect to handle this.
578
switch (tag) {
579
case 'input':
585
- ReactDOMFiberInput.postMountWrapper(workInProgress, props);
580
+ ReactDOMFiberInput.postMountWrapper(domElement, props);
581
if (props.autoFocus) {
587
- focusNode(getNode(workInProgress));
582
+ focusNode(domElement);
583
}
584
break;
585
case 'textarea':
591
- ReactDOMFiberTextarea.postMountWrapper(workInProgress, props);
586
+ ReactDOMFiberTextarea.postMountWrapper(domElement, props);
587
if (props.autoFocus) {
593
- focusNode(getNode(workInProgress));
588
+ focusNode(domElement);
589
}
590
break;
591
case 'select':
592
if (props.autoFocus) {
598
- focusNode(getNode(workInProgress));
593
+ focusNode(domElement);
594
}
595
break;
596
case 'button':
597
if (props.autoFocus) {
603
- focusNode(getNode(workInProgress));
598
+ focusNode(domElement);
599
}
600
break;
601
case 'option':
607
- ReactDOMFiberOption.postMountWrapper(workInProgress, props);
602
+ ReactDOMFiberOption.postMountWrapper(domElement, props);
603
break;
604
default:
605
if (typeof props.onClick === 'function') {
611
- trapClickOnNonInteractiveElement(workInProgress);
606
+ trapClickOnNonInteractiveElement(domElement);
607
}
608
break;
609
}
610
616
- return el;
611
+ return domElement;
612
},
613
614
receiveComponent: function(
620
- workInProgress : Fiber,
615
+ domElement : Element,
616
rootContainerElement : Element,
617
tag : string,
618
+ lastProps : Object,
619
nextProps : Object,
620
context : Object
621
) {
626
- var lastProps = workInProgress.memoizedProps;
627
-
622
switch (tag) {
623
case 'input':
630
- lastProps = ReactDOMFiberInput.getHostProps(workInProgress, lastProps);
631
- nextProps = ReactDOMFiberInput.getHostProps(workInProgress, nextProps);
624
+ lastProps = ReactDOMFiberInput.getHostProps(domElement, lastProps);
625
+ nextProps = ReactDOMFiberInput.getHostProps(domElement, nextProps);
626
break;
627
case 'option':
634
- lastProps = ReactDOMFiberOption.getHostProps(workInProgress, lastProps);
635
- nextProps = ReactDOMFiberOption.getHostProps(workInProgress, nextProps);
628
+ lastProps = ReactDOMFiberOption.getHostProps(domElement, lastProps);
629
+ nextProps = ReactDOMFiberOption.getHostProps(domElement, nextProps);
630
break;
631
case 'select':
638
- lastProps = ReactDOMFiberSelect.getHostProps(workInProgress, lastProps);
639
- nextProps = ReactDOMFiberSelect.getHostProps(workInProgress, nextProps);
632
+ lastProps = ReactDOMFiberSelect.getHostProps(domElement, lastProps);
633
+ nextProps = ReactDOMFiberSelect.getHostProps(domElement, nextProps);
634
break;
635
case 'textarea':
642
- lastProps = ReactDOMFiberTextarea.getHostProps(workInProgress, lastProps);
643
- nextProps = ReactDOMFiberTextarea.getHostProps(workInProgress, nextProps);
636
+ lastProps = ReactDOMFiberTextarea.getHostProps(domElement, lastProps);
637
+ nextProps = ReactDOMFiberTextarea.getHostProps(domElement, nextProps);
638
break;
639
default:
640
if (typeof lastProps.onClick !== 'function' &&
641
typeof nextProps.onClick === 'function') {
648
- trapClickOnNonInteractiveElement(workInProgress);
642
+ trapClickOnNonInteractiveElement(domElement);
643
}
644
break;
645
}
@@ -654,7 +648,7 @@ var ReactDOMFiberComponent = {
648
var wasCustomComponentTag = isCustomComponent(tag, lastProps);
649
var isCustomComponentTag = isCustomComponent(tag, nextProps);
650
updateDOMProperties(
657
- workInProgress,
651
+ domElement,
652
rootContainerElement,
653
lastProps,
654
nextProps,
@@ -667,38 +661,33 @@ var ReactDOMFiberComponent = {
661
// Update the wrapper around inputs *after* updating props. This has to
662
// happen after `updateDOMProperties`. Otherwise HTML5 input validations
663
// raise warnings and prevent the new value from being assigned.
670
- ReactDOMFiberInput.updateWrapper(workInProgress, nextProps);
664
+ ReactDOMFiberInput.updateWrapper(domElement, nextProps);
665
break;
666
case 'textarea':
673
- ReactDOMFiberTextarea.updateWrapper(workInProgress, nextProps);
667
+ ReactDOMFiberTextarea.updateWrapper(domElement, nextProps);
668
break;
669
case 'select':
670
// <select> value update needs to occur after <option> children
671
// reconciliation
678
- ReactDOMFiberSelect.postUpdateWrapper(workInProgress, nextProps);
672
+ ReactDOMFiberSelect.postUpdateWrapper(domElement, nextProps);
673
break;
674
}
675
},
676
683
- restoreControlledState: function(finishedWork : Fiber, props : Object) {
684
- switch (finishedWork.type) {
677
+ restoreControlledState: function(domElement : Element, tag : string, props : Object) {
678
+ switch (tag) {
679
case 'input':
686
- ReactDOMFiberInput.restoreControlledState(finishedWork, props);
680
+ ReactDOMFiberInput.restoreControlledState(domElement, props);
681
return;
682
case 'textarea':
689
- ReactDOMFiberTextarea.restoreControlledState(finishedWork, props);
683
+ ReactDOMFiberTextarea.restoreControlledState(domElement, props);
684
return;
685
case 'select':
692
- ReactDOMFiberSelect.restoreControlledState(finishedWork, props);
686
+ ReactDOMFiberSelect.restoreControlledState(domElement, props);
687
return;
688
}
689
},
690
697
- getPublicInstance: function(fiber : Fiber) {
698
- // If we add wrappers, this could be something deeper.
699
- return fiber.stateNode;
700
- },
701
-
691
};
692
693
module.exports = ReactDOMFiberComponent;
src/renderers/dom/fiber/wrappers/ReactDOMFiberInput.js
+31
-25
@@ -12,7 +12,13 @@
12
13
'use strict';
14
15
-import type { Fiber } from 'ReactFiber';
15
+type InputWithWrapperState = HTMLInputElement & {
16
+ _wrapperState: {
17
+ initialValue: ?string,
18
+ initialChecked: ?boolean,
19
+ controlled?: boolean
20
+ }
21
+};
22
23
var DOMPropertyOperations = require('DOMPropertyOperations');
24
var ReactControlledValuePropTypes = require('ReactControlledValuePropTypes');
@@ -49,7 +55,8 @@ function isControlled(props) {
55
* See http://www.w3.org/TR/2012/WD-html5-20121025/the-input-element.html
56
*/
57
var ReactDOMInput = {
52
- getHostProps: function(inst : Fiber, props : Object) {
58
+ getHostProps: function(element : Element, props : Object) {
59
+ var node = ((element : any) : InputWithWrapperState);
60
var value = props.value;
61
var checked = props.checked;
62
@@ -67,14 +74,14 @@ var ReactDOMInput = {
74
}, props, {
75
defaultChecked: undefined,
76
defaultValue: undefined,
70
- value: value != null ? value : inst._wrapperState.initialValue,
71
- checked: checked != null ? checked : inst._wrapperState.initialChecked,
77
+ value: value != null ? value : node._wrapperState.initialValue,
78
+ checked: checked != null ? checked : node._wrapperState.initialChecked,
79
});
80
81
return hostProps;
82
},
83
77
- mountWrapper: function(inst : Fiber, props : Object) {
84
+ mountWrapper: function(element : Element, props : Object) {
85
if (__DEV__) {
86
ReactControlledValuePropTypes.checkPropTypes(
87
'input',
@@ -121,21 +128,23 @@ var ReactDOMInput = {
128
}
129
130
var defaultValue = props.defaultValue;
124
- inst._wrapperState = {
131
+ var node = ((element : any) : InputWithWrapperState);
132
+ node._wrapperState = {
133
initialChecked: props.checked != null ? props.checked : props.defaultChecked,
134
initialValue: props.value != null ? props.value : defaultValue,
135
};
136
137
if (__DEV__) {
130
- inst._wrapperState.controlled = isControlled(props);
138
+ node._wrapperState.controlled = isControlled(props);
139
}
140
},
141
134
- updateWrapper: function(inst : Fiber, props : Object) {
142
+ updateWrapper: function(element : Element, props : Object) {
143
+ var node = ((element : any) : InputWithWrapperState);
144
if (__DEV__) {
145
var controlled = isControlled(props);
146
138
- if (!inst._wrapperState.controlled && controlled && !didWarnUncontrolledToControlled) {
147
+ if (!node._wrapperState.controlled && controlled && !didWarnUncontrolledToControlled) {
148
warning(
149
false,
150
'%s is changing an uncontrolled input of type %s to be controlled. ' +
@@ -147,7 +156,7 @@ var ReactDOMInput = {
156
);
157
didWarnUncontrolledToControlled = true;
158
}
150
- if (inst._wrapperState.controlled && !controlled && !didWarnControlledToUncontrolled) {
159
+ if (node._wrapperState.controlled && !controlled && !didWarnControlledToUncontrolled) {
160
warning(
161
false,
162
'%s is changing a controlled input of type %s to be uncontrolled. ' +
@@ -164,13 +173,12 @@ var ReactDOMInput = {
173
var checked = props.checked;
174
if (checked != null) {
175
DOMPropertyOperations.setValueForProperty(
167
- ReactDOMComponentTree.getNodeFromInstance(inst),
176
+ node,
177
'checked',
178
checked || false
179
);
180
}
181
173
- var node = ReactDOMComponentTree.getNodeFromInstance(inst);
182
var value = props.value;
183
if (value != null) {
184
@@ -202,10 +210,8 @@ var ReactDOMInput = {
210
}
211
},
212
205
- postMountWrapper: function(inst : Fiber, props : Object) {
206
- // This is in postMount because we need access to the DOM node, which is not
207
- // available until after the component has mounted.
208
- var node = ReactDOMComponentTree.getNodeFromInstance(inst);
213
+ postMountWrapper: function(element : Element, props : Object) {
214
+ var node = ((element : any) : InputWithWrapperState);
215
216
// Detach value from defaultValue. We won't do anything if we're working on
217
// submit or reset inputs as those values & defaultValues are linked. They
@@ -250,20 +256,20 @@ var ReactDOMInput = {
256
}
257
},
258
253
- restoreControlledState: function(inst : Fiber, props : Object) {
254
- ReactDOMInput.updateWrapper(inst, props);
255
- updateNamedCousins(inst, props);
259
+ restoreControlledState: function(element : Element, props : Object) {
260
+ var node = ((element : any) : InputWithWrapperState);
261
+ ReactDOMInput.updateWrapper(node, props);
262
+ updateNamedCousins(node, props);
263
},
264
};
265
259
-function updateNamedCousins(thisInstance, props) {
266
+function updateNamedCousins(rootNode, props) {
267
var name = props.name;
268
if (props.type === 'radio' && name != null) {
262
- var rootNode = ReactDOMComponentTree.getNodeFromInstance(thisInstance);
263
- var queryRoot = rootNode;
269
+ var queryRoot : Element = rootNode;
270
271
while (queryRoot.parentNode) {
266
- queryRoot = queryRoot.parentNode;
272
+ queryRoot = ((queryRoot.parentNode : any) : Element);
273
}
274
275
// If `rootNode.form` was non-null, then we could try `form.elements`,
@@ -277,7 +283,7 @@ function updateNamedCousins(thisInstance, props) {
283
'input[name=' + JSON.stringify('' + name) + '][type="radio"]');
284
285
for (var i = 0; i < group.length; i++) {
280
- var otherNode = group[i];
286
+ var otherNode = ((group[i] : any) : HTMLInputElement);
287
if (otherNode === rootNode ||
288
otherNode.form !== rootNode.form) {
289
continue;
@@ -296,7 +302,7 @@ function updateNamedCousins(thisInstance, props) {
302
// If this is a controlled radio button group, forcing the input that
303
// was previously checked to update will cause it to be come re-checked
304
// as appropriate.
299
- ReactDOMInput.updateWrapper(otherInstance, otherInstance.memoizedProps);
305
+ ReactDOMInput.updateWrapper(otherNode, otherInstance.memoizedProps);
306
}
307
}
308
}
src/renderers/dom/fiber/wrappers/ReactDOMFiberOption.js
+4
-8
@@ -12,10 +12,7 @@
12
13
'use strict';
14
15
-import type { Fiber } from 'ReactFiber';
16
-
15
var React = require('React');
18
-var ReactDOMComponentTree = require('ReactDOMComponentTree');
16
17
var warning = require('warning');
18
var didWarnInvalidOptionChildren = false;
@@ -47,7 +44,7 @@ function flattenChildren(children) {
44
* Implements an <option> host component that warns when `selected` is set.
45
*/
46
var ReactDOMOption = {
50
- mountWrapper: function(inst : Fiber, props : Object) {
47
+ mountWrapper: function(element : Element, props : Object) {
48
// TODO (yungsters): Remove support for `selected` in <option>.
49
if (__DEV__) {
50
warning(
@@ -58,15 +55,14 @@ var ReactDOMOption = {
55
}
56
},
57
61
- postMountWrapper: function(inst : Fiber, props : Object) {
58
+ postMountWrapper: function(element : Element, props : Object) {
59
// value="" should make a value attribute (#6219)
60
if (props.value != null) {
64
- var node = ReactDOMComponentTree.getNodeFromInstance(inst);
65
- node.setAttribute('value', props.value);
61
+ element.setAttribute('value', props.value);
62
}
63
},
64
69
- getHostProps: function(inst : Fiber, props : Object) {
65
+ getHostProps: function(element : Element, props : Object) {
66
var hostProps = Object.assign({children: undefined}, props);
67
68
var content = flattenChildren(props.children);
src/renderers/dom/fiber/wrappers/ReactDOMFiberSelect.js
+26
-18
@@ -12,10 +12,14 @@
12
13
'use strict';
14
15
-import type { Fiber } from 'ReactFiber';
15
+type SelectWithWrapperState = HTMLSelectElement & {
16
+ _wrapperState: {
17
+ initialValue: ?string,
18
+ wasMultiple: boolean
19
+ }
20
+};
21
22
var ReactControlledValuePropTypes = require('ReactControlledValuePropTypes');
18
-var ReactDOMComponentTree = require('ReactDOMComponentTree');
23
24
var getCurrentOwnerName = require('getCurrentOwnerName');
25
var warning = require('warning');
@@ -35,7 +39,7 @@ var valuePropNames = ['value', 'defaultValue'];
39
/**
40
* Validation function for `value` and `defaultValue`.
41
*/
38
-function checkSelectPropTypes(inst, props) {
42
+function checkSelectPropTypes(props) {
43
ReactControlledValuePropTypes.checkPropTypes(
44
'select',
45
props,
@@ -68,8 +72,9 @@ function checkSelectPropTypes(inst, props) {
72
}
73
}
74
71
-function updateOptions(inst : Fiber, multiple : boolean, propValue : any) {
72
- var options = ReactDOMComponentTree.getNodeFromInstance(inst).options;
75
+function updateOptions(node : HTMLSelectElement, multiple : boolean, propValue : any) {
76
+ type IndexableHTMLOptionsCollection = HTMLOptionsCollection & { [key:number]: HTMLOptionElement };
77
+ var options : IndexableHTMLOptionsCollection = node.options;
78
79
if (multiple) {
80
let selectedValues = (propValue : Array<string>);
@@ -115,19 +120,20 @@ function updateOptions(inst : Fiber, multiple : boolean, propValue : any) {
120
* selected.
121
*/
122
var ReactDOMSelect = {
118
- getHostProps: function(inst : Fiber, props : Object) {
123
+ getHostProps: function(element : Element, props : Object) {
124
return Object.assign({}, props, {
125
value: undefined,
126
});
127
},
128
124
- mountWrapper: function(inst : Fiber, props : Object) {
129
+ mountWrapper: function(element : Element, props : Object) {
130
+ var node = ((element : any) : SelectWithWrapperState);
131
if (__DEV__) {
126
- checkSelectPropTypes(inst, props);
132
+ checkSelectPropTypes(props);
133
}
134
135
var value = props.value;
130
- inst._wrapperState = {
136
+ node._wrapperState = {
137
initialValue: value != null ? value : props.defaultValue,
138
wasMultiple: Boolean(props.multiple),
139
};
@@ -149,33 +155,35 @@ var ReactDOMSelect = {
155
}
156
},
157
152
- postUpdateWrapper: function(inst : Fiber, props : Object) {
158
+ postUpdateWrapper: function(element : Element, props : Object) {
159
+ var node = ((element : any) : SelectWithWrapperState);
160
// After the initial mount, we control selected-ness manually so don't pass
161
// this value down
155
- inst._wrapperState.initialValue = undefined;
162
+ node._wrapperState.initialValue = undefined;
163
157
- var wasMultiple = inst._wrapperState.wasMultiple;
158
- inst._wrapperState.wasMultiple = Boolean(props.multiple);
164
+ var wasMultiple = node._wrapperState.wasMultiple;
165
+ node._wrapperState.wasMultiple = Boolean(props.multiple);
166
167
var value = props.value;
168
if (value != null) {
162
- updateOptions(inst, Boolean(props.multiple), value);
169
+ updateOptions(node, Boolean(props.multiple), value);
170
} else if (wasMultiple !== Boolean(props.multiple)) {
171
// For simplicity, reapply `defaultValue` if `multiple` is toggled.
172
if (props.defaultValue != null) {
166
- updateOptions(inst, Boolean(props.multiple), props.defaultValue);
173
+ updateOptions(node, Boolean(props.multiple), props.defaultValue);
174
} else {
175
// Revert the select back to its default unselected state.
169
- updateOptions(inst, Boolean(props.multiple), props.multiple ? [] : '');
176
+ updateOptions(node, Boolean(props.multiple), props.multiple ? [] : '');
177
}
178
}
179
},
180
174
- restoreControlledState: function(inst : Fiber, props : Object) {
181
+ restoreControlledState: function(element : Element, props : Object) {
182
+ var node = ((element : any) : SelectWithWrapperState);
183
var value = props.value;
184
185
if (value != null) {
178
- updateOptions(inst, Boolean(props.multiple), value);
186
+ updateOptions(node, Boolean(props.multiple), value);
187
}
188
},
189
};
src/renderers/dom/fiber/wrappers/ReactDOMFiberTextarea.js
+18
-13
@@ -12,10 +12,13 @@
12
13
'use strict';
14
15
-import type { Fiber } from 'ReactFiber';
15
+type TextAreaWithWrapperState = HTMLTextAreaElement & {
16
+ _wrapperState: {
17
+ initialValue: string,
18
+ }
19
+};
20
21
var ReactControlledValuePropTypes = require('ReactControlledValuePropTypes');
18
-var ReactDOMComponentTree = require('ReactDOMComponentTree');
22
23
var getCurrentOwnerName = require('getCurrentOwnerName');
24
var invariant = require('invariant');
@@ -39,7 +42,8 @@ var didWarnValDefaultVal = false;
42
* `defaultValue` if specified, or the children content (deprecated).
43
*/
44
var ReactDOMTextarea = {
42
- getHostProps: function(inst : Fiber, props : Object) {
45
+ getHostProps: function(element : Element, props : Object) {
46
+ var node = ((element : any) : TextAreaWithWrapperState);
47
invariant(
48
props.dangerouslySetInnerHTML == null,
49
'`dangerouslySetInnerHTML` does not make sense on <textarea>.'
@@ -53,13 +57,14 @@ var ReactDOMTextarea = {
57
var hostProps = Object.assign({}, props, {
58
value: undefined,
59
defaultValue: undefined,
56
- children: '' + inst._wrapperState.initialValue,
60
+ children: '' + node._wrapperState.initialValue,
61
});
62
63
return hostProps;
64
},
65
62
- mountWrapper: function(inst : Fiber, props : Object) {
66
+ mountWrapper: function(element : Element, props : Object) {
67
+ var node = ((element : any) : TextAreaWithWrapperState);
68
if (__DEV__) {
69
ReactControlledValuePropTypes.checkPropTypes(
70
'textarea',
@@ -120,13 +125,13 @@ var ReactDOMTextarea = {
125
initialValue = defaultValue;
126
}
127
123
- inst._wrapperState = {
128
+ node._wrapperState = {
129
initialValue: '' + initialValue,
130
};
131
},
132
128
- updateWrapper: function(inst : Fiber, props : Object) {
129
- var node = ReactDOMComponentTree.getNodeFromInstance(inst);
133
+ updateWrapper: function(element : Element, props : Object) {
134
+ var node = ((element : any) : TextAreaWithWrapperState);
135
var value = props.value;
136
if (value != null) {
137
// Cast `value` to a string to ensure the value is set correctly. While
@@ -146,24 +151,24 @@ var ReactDOMTextarea = {
151
}
152
},
153
149
- postMountWrapper: function(inst : Fiber, props : Object) {
154
+ postMountWrapper: function(element : Element, props : Object) {
155
+ var node = ((element : any) : TextAreaWithWrapperState);
156
// This is in postMount because we need access to the DOM node, which is not
157
// available until after the component has mounted.
152
- var node = ReactDOMComponentTree.getNodeFromInstance(inst);
158
var textContent = node.textContent;
159
160
// Only set node.value if textContent is equal to the expected
161
// initial value. In IE10/IE11 there is a bug where the placeholder attribute
162
// will populate textContent as well.
163
// https://developer.microsoft.com/microsoft-edge/platform/issues/101525/
159
- if (textContent === inst._wrapperState.initialValue) {
164
+ if (textContent === node._wrapperState.initialValue) {
165
node.value = textContent;
166
}
167
},
168
164
- restoreControlledState: function(inst : Fiber, props : Object) {
169
+ restoreControlledState: function(element : Element, props : Object) {
170
// DOM component is still mounted; update
166
- ReactDOMTextarea.updateWrapper(inst, props);
171
+ ReactDOMTextarea.updateWrapper(element, props);
172
},
173
174
};