7
* @flow
8
*/
9
10
+import type {InputWithWrapperState} from './ReactDOMInput';
11
+
12
import {
13
registrationNameDependencies,
14
possibleRegistrationNames,
27
} from './DOMPropertyOperations';
28
import {
29
initWrapperState as ReactDOMInputInitWrapperState,
28
- getHostProps as ReactDOMInputGetHostProps,
30
postMountWrapper as ReactDOMInputPostMountWrapper,
31
updateChecked as ReactDOMInputUpdateChecked,
32
updateWrapper as ReactDOMInputUpdateWrapper,
38
} from './ReactDOMOption';
39
import {
40
initWrapperState as ReactDOMSelectInitWrapperState,
40
- getHostProps as ReactDOMSelectGetHostProps,
41
postMountWrapper as ReactDOMSelectPostMountWrapper,
42
restoreControlledState as ReactDOMSelectRestoreControlledState,
43
postUpdateWrapper as ReactDOMSelectPostUpdateWrapper,
44
} from './ReactDOMSelect';
45
import {
46
initWrapperState as ReactDOMTextareaInitWrapperState,
47
- getHostProps as ReactDOMTextareaGetHostProps,
47
postMountWrapper as ReactDOMTextareaPostMountWrapper,
48
updateWrapper as ReactDOMTextareaUpdateWrapper,
49
restoreControlledState as ReactDOMTextareaRestoreControlledState,
286
node.onclick = noop;
287
}
288
290
-function setInitialDOMProperties(
291
- tag: string,
289
+function setProp(
290
domElement: Element,
293
- nextProps: Object,
291
+ tag: string,
292
+ key: string,
293
+ value: mixed,
294
isCustomComponentTag: boolean,
295
+ props: any,
296
): void {
296
- for (const propKey in nextProps) {
297
- if (!nextProps.hasOwnProperty(propKey)) {
298
- continue;
297
+ switch (key) {
298
+ case 'style': {
299
+ if (value != null && typeof value !== 'object') {
300
+ throw new Error(
301
+ 'The `style` prop expects a mapping from style properties to values, ' +
302
+ "not a string. For example, style={{marginRight: spacing + 'em'}} when " +
303
+ 'using JSX.',
304
+ );
305
+ }
306
+ if (__DEV__) {
307
+ if (value) {
308
+ // Freeze the next style object so that we can assume it won't be
309
+ // mutated. We have already warned for this in the past.
310
+ Object.freeze(value);
311
+ }
312
+ }
313
+ // Relies on `updateStylesByID` not mutating `styleUpdates`.
314
+ setValueForStyles(domElement, value);
315
+ break;
316
}
300
- const nextProp = nextProps[propKey];
301
- switch (propKey) {
302
- case 'style': {
303
- if (nextProp != null && typeof nextProp !== 'object') {
317
+ case 'dangerouslySetInnerHTML': {
318
+ if (value != null) {
319
+ if (typeof value !== 'object' || !('__html' in value)) {
320
throw new Error(
305
- 'The `style` prop expects a mapping from style properties to values, ' +
306
- "not a string. For example, style={{marginRight: spacing + 'em'}} when " +
307
- 'using JSX.',
321
+ '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
322
+ 'Please visit https://reactjs.org/link/dangerously-set-inner-html ' +
323
+ 'for more information.',
324
);
325
}
310
- if (__DEV__) {
311
- if (nextProp) {
312
- // Freeze the next style object so that we can assume it won't be
313
- // mutated. We have already warned for this in the past.
314
- Object.freeze(nextProp);
315
- }
316
- }
317
- // Relies on `updateStylesByID` not mutating `styleUpdates`.
318
- setValueForStyles(domElement, nextProp);
319
- break;
320
- }
321
- case 'dangerouslySetInnerHTML': {
322
- if (nextProp != null) {
323
- if (typeof nextProp !== 'object' || !('__html' in nextProp)) {
326
+ const nextHtml: any = value.__html;
327
+ if (nextHtml != null) {
328
+ if (props.children != null) {
329
throw new Error(
325
- '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
326
- 'Please visit https://reactjs.org/link/dangerously-set-inner-html ' +
327
- 'for more information.',
330
+ 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
331
);
332
}
330
- const nextHtml = nextProp.__html;
331
- if (nextHtml != null) {
332
- if (nextProps.children != null) {
333
- throw new Error(
334
- 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
335
- );
336
- }
337
- if (disableIEWorkarounds) {
338
- domElement.innerHTML = nextHtml;
339
- } else {
340
- setInnerHTML(domElement, nextHtml);
341
- }
333
+ if (disableIEWorkarounds) {
334
+ domElement.innerHTML = nextHtml;
335
+ } else {
336
+ setInnerHTML(domElement, nextHtml);
337
}
338
}
344
- break;
339
}
346
- case 'children': {
347
- if (typeof nextProp === 'string') {
348
- // Avoid setting initial textContent when the text is empty. In IE11 setting
349
- // textContent on a <textarea> will cause the placeholder to not
350
- // show within the <textarea> until it has been focused and blurred again.
351
- // https://github.com/facebook/react/issues/6731#issuecomment-254874553
352
- const canSetTextContent =
353
- (!enableHostSingletons || tag !== 'body') &&
354
- (tag !== 'textarea' || nextProp !== '');
355
- if (canSetTextContent) {
356
- setTextContent(domElement, nextProp);
357
- }
358
- } else if (typeof nextProp === 'number') {
359
- const canSetTextContent = !enableHostSingletons || tag !== 'body';
360
- if (canSetTextContent) {
361
- setTextContent(domElement, '' + nextProp);
362
- }
340
+ break;
341
+ }
342
+ case 'children': {
343
+ if (typeof value === 'string') {
344
+ // Avoid setting initial textContent when the text is empty. In IE11 setting
345
+ // textContent on a <textarea> will cause the placeholder to not
346
+ // show within the <textarea> until it has been focused and blurred again.
347
+ // https://github.com/facebook/react/issues/6731#issuecomment-254874553
348
+ const canSetTextContent =
349
+ (!enableHostSingletons || tag !== 'body') &&
350
+ (tag !== 'textarea' || value !== '');
351
+ if (canSetTextContent) {
352
+ setTextContent(domElement, value);
353
}
364
- break;
365
- }
366
- case 'onScroll': {
367
- if (nextProp != null) {
368
- if (__DEV__ && typeof nextProp !== 'function') {
369
- warnForInvalidEventListener(propKey, nextProp);
370
- }
371
- listenToNonDelegatedEvent('scroll', domElement);
354
+ } else if (typeof value === 'number') {
355
+ const canSetTextContent = !enableHostSingletons || tag !== 'body';
356
+ if (canSetTextContent) {
357
+ setTextContent(domElement, '' + value);
358
}
373
- break;
359
}
375
- case 'suppressContentEditableWarning':
376
- case 'suppressHydrationWarning':
377
- case 'defaultValue': // Reserved
378
- case 'defaultChecked':
379
- case 'innerHTML': {
380
- // Noop
381
- break;
382
- }
383
- case 'autoFocus': {
384
- // We polyfill it separately on the client during commit.
385
- // We could have excluded it in the property list instead of
386
- // adding a special case here, but then it wouldn't be emitted
387
- // on server rendering (but we *do* want to emit it in SSR).
388
- break;
389
- }
390
- case 'innerText': // Properties
391
- case 'textContent':
392
- if (enableCustomElementPropertySupport) {
393
- break;
360
+ break;
361
+ }
362
+ case 'onScroll': {
363
+ if (value != null) {
364
+ if (__DEV__ && typeof value !== 'function') {
365
+ warnForInvalidEventListener(key, value);
366
}
395
- // eslint-disable-next-line no-fallthrough
396
- default: {
397
- if (registrationNameDependencies.hasOwnProperty(propKey)) {
398
- if (nextProp != null) {
399
- if (__DEV__ && typeof nextProp !== 'function') {
400
- warnForInvalidEventListener(propKey, nextProp);
401
- }
402
- }
403
- } else if (nextProp != null) {
404
- if (isCustomComponentTag) {
405
- setValueForPropertyOnCustomComponent(domElement, propKey, nextProp);
406
- } else {
407
- setValueForProperty(domElement, propKey, nextProp);
408
- }
367
+ listenToNonDelegatedEvent('scroll', domElement);
368
+ }
369
+ break;
370
+ }
371
+ case 'onClick': {
372
+ // TODO: This cast may not be sound for SVG, MathML or custom elements.
373
+ if (value != null) {
374
+ if (__DEV__ && typeof value !== 'function') {
375
+ warnForInvalidEventListener(key, value);
376
}
377
+ trapClickOnNonInteractiveElement(((domElement: any): HTMLElement));
378
}
379
+ break;
380
}
412
- }
413
-}
414
-
415
-function updateDOMProperties(
416
- domElement: Element,
417
- updatePayload: Array<any>,
418
- wasCustomComponentTag: boolean,
419
- isCustomComponentTag: boolean,
420
-): void {
421
- // TODO: Handle wasCustomComponentTag
422
- for (let i = 0; i < updatePayload.length; i += 2) {
423
- const propKey = updatePayload[i];
424
- const propValue = updatePayload[i + 1];
425
- switch (propKey) {
426
- case 'style':
427
- setValueForStyles(domElement, propValue);
381
+ case 'suppressContentEditableWarning':
382
+ case 'suppressHydrationWarning':
383
+ case 'defaultValue': // Reserved
384
+ case 'defaultChecked':
385
+ case 'innerHTML': {
386
+ // Noop
387
+ break;
388
+ }
389
+ case 'autoFocus': {
390
+ // We polyfill it separately on the client during commit.
391
+ // We could have excluded it in the property list instead of
392
+ // adding a special case here, but then it wouldn't be emitted
393
+ // on server rendering (but we *do* want to emit it in SSR).
394
+ break;
395
+ }
396
+ case 'innerText': // Properties
397
+ case 'textContent':
398
+ if (enableCustomElementPropertySupport) {
399
break;
429
- case 'dangerouslySetInnerHTML':
430
- if (disableIEWorkarounds) {
431
- domElement.innerHTML = propValue;
432
- } else {
433
- setInnerHTML(domElement, propValue);
400
+ }
401
+ // eslint-disable-next-line no-fallthrough
402
+ default: {
403
+ if (registrationNameDependencies.hasOwnProperty(key)) {
404
+ if (__DEV__ && value != null && typeof value !== 'function') {
405
+ warnForInvalidEventListener(key, value);
406
}
435
- break;
436
- case 'children':
437
- setTextContent(domElement, propValue);
438
- break;
439
- default:
407
+ } else {
408
if (isCustomComponentTag) {
441
- setValueForPropertyOnCustomComponent(domElement, propKey, propValue);
409
+ setValueForPropertyOnCustomComponent(domElement, key, value);
410
} else {
443
- setValueForProperty(domElement, propKey, propValue);
411
+ setValueForProperty(domElement, key, value);
412
}
445
- break;
413
+ }
414
}
415
}
416
}
548
export function setInitialProperties(
549
domElement: Element,
550
tag: string,
583
- rawProps: Object,
551
+ props: Object,
552
): void {
585
- const isCustomComponentTag = isCustomComponent(tag, rawProps);
553
if (__DEV__) {
587
- validatePropertiesInDevelopment(tag, rawProps);
554
+ validatePropertiesInDevelopment(tag, props);
555
}
556
557
// TODO: Make sure that we check isMounted before firing any of these events.
591
- let props: Object;
558
+
559
switch (tag) {
593
- case 'dialog':
560
+ case 'input': {
561
+ ReactDOMInputInitWrapperState(domElement, props);
562
+ // We listen to this event in case to ensure emulated bubble
563
+ // listeners still fire for the invalid event.
564
+ listenToNonDelegatedEvent('invalid', domElement);
565
+ for (const propKey in props) {
566
+ if (!props.hasOwnProperty(propKey)) {
567
+ continue;
568
+ }
569
+ const propValue = props[propKey];
570
+ if (propValue == null) {
571
+ continue;
572
+ }
573
+ switch (propKey) {
574
+ case 'checked': {
575
+ const node = ((domElement: any): InputWithWrapperState);
576
+ const checked =
577
+ propValue != null ? propValue : node._wrapperState.initialChecked;
578
+ node.checked =
579
+ !!checked &&
580
+ typeof checked !== 'function' &&
581
+ checked !== 'symbol';
582
+ break;
583
+ }
584
+ case 'value': {
585
+ // This is handled by updateWrapper below.
586
+ break;
587
+ }
588
+ case 'children':
589
+ case 'dangerouslySetInnerHTML': {
590
+ if (propValue != null) {
591
+ throw new Error(
592
+ `${tag} is a void element tag and must neither have \`children\` nor ` +
593
+ 'use `dangerouslySetInnerHTML`.',
594
+ );
595
+ }
596
+ break;
597
+ }
598
+ // defaultChecked and defaultValue are ignored by setProp
599
+ default: {
600
+ setProp(domElement, tag, propKey, propValue, false, props);
601
+ }
602
+ }
603
+ }
604
+ // TODO: Make sure we check if this is still unmounted or do any clean
605
+ // up necessary since we never stop tracking anymore.
606
+ track((domElement: any));
607
+ ReactDOMInputPostMountWrapper(domElement, props, false);
608
+ return;
609
+ }
610
+ case 'select': {
611
+ ReactDOMSelectInitWrapperState(domElement, props);
612
+ // We listen to this event in case to ensure emulated bubble
613
+ // listeners still fire for the invalid event.
614
+ listenToNonDelegatedEvent('invalid', domElement);
615
+ for (const propKey in props) {
616
+ if (!props.hasOwnProperty(propKey)) {
617
+ continue;
618
+ }
619
+ const propValue = props[propKey];
620
+ if (propValue == null) {
621
+ continue;
622
+ }
623
+ switch (propKey) {
624
+ case 'value': {
625
+ // This is handled by updateWrapper below.
626
+ break;
627
+ }
628
+ // defaultValue are ignored by setProp
629
+ default: {
630
+ setProp(domElement, tag, propKey, propValue, false, props);
631
+ }
632
+ }
633
+ }
634
+ ReactDOMSelectPostMountWrapper(domElement, props);
635
+ return;
636
+ }
637
+ case 'textarea': {
638
+ ReactDOMTextareaInitWrapperState(domElement, props);
639
+ // We listen to this event in case to ensure emulated bubble
640
+ // listeners still fire for the invalid event.
641
+ listenToNonDelegatedEvent('invalid', domElement);
642
+ for (const propKey in props) {
643
+ if (!props.hasOwnProperty(propKey)) {
644
+ continue;
645
+ }
646
+ const propValue = props[propKey];
647
+ if (propValue == null) {
648
+ continue;
649
+ }
650
+ switch (propKey) {
651
+ case 'value': {
652
+ // This is handled by updateWrapper below.
653
+ break;
654
+ }
655
+ case 'children': {
656
+ // TODO: Handled by initWrapperState above.
657
+ break;
658
+ }
659
+ case 'dangerouslySetInnerHTML': {
660
+ if (propValue != null) {
661
+ // TODO: Do we really need a special error message for this. It's also pretty blunt.
662
+ throw new Error(
663
+ '`dangerouslySetInnerHTML` does not make sense on <textarea>.',
664
+ );
665
+ }
666
+ break;
667
+ }
668
+ // defaultValue is ignored by setProp
669
+ default: {
670
+ setProp(domElement, tag, propKey, propValue, false, props);
671
+ }
672
+ }
673
+ }
674
+ // TODO: Make sure we check if this is still unmounted or do any clean
675
+ // up necessary since we never stop tracking anymore.
676
+ track((domElement: any));
677
+ ReactDOMTextareaPostMountWrapper(domElement, props);
678
+ return;
679
+ }
680
+ case 'option': {
681
+ ReactDOMOptionValidateProps(domElement, props);
682
+ for (const propKey in props) {
683
+ if (!props.hasOwnProperty(propKey)) {
684
+ continue;
685
+ }
686
+ const propValue = props[propKey];
687
+ if (propValue == null) {
688
+ continue;
689
+ }
690
+ setProp(domElement, tag, propKey, propValue, false, props);
691
+ }
692
+ ReactDOMOptionPostMountWrapper(domElement, props);
693
+ return;
694
+ }
695
+ case 'dialog': {
696
listenToNonDelegatedEvent('cancel', domElement);
697
listenToNonDelegatedEvent('close', domElement);
596
- props = rawProps;
698
break;
598
- case 'embed':
599
- if (
600
- rawProps.children != null ||
601
- rawProps.dangerouslySetInnerHTML != null
602
- ) {
603
- // TODO: Can we make this a DEV warning to avoid this deny list?
604
- throw new Error(
605
- `${tag} is a void element tag and must neither have \`children\` nor ` +
606
- 'use `dangerouslySetInnerHTML`.',
607
- );
608
- }
609
- // eslint-disable-next-line no-fallthrough
699
+ }
700
case 'iframe':
611
- case 'object':
701
+ case 'object': {
702
// We listen to this event in case to ensure emulated bubble
703
// listeners still fire for the load event.
704
listenToNonDelegatedEvent('load', domElement);
615
- props = rawProps;
705
break;
706
+ }
707
case 'video':
618
- case 'audio':
708
+ case 'audio': {
709
// We listen to these events in case to ensure emulated bubble
710
// listeners still fire for all the media events.
711
for (let i = 0; i < mediaEventTypes.length; i++) {
712
listenToNonDelegatedEvent(mediaEventTypes[i], domElement);
713
}
624
- props = rawProps;
714
break;
626
- case 'source':
627
- if (
628
- rawProps.children != null ||
629
- rawProps.dangerouslySetInnerHTML != null
630
- ) {
631
- // TODO: Can we make this a DEV warning to avoid this deny list?
632
- throw new Error(
633
- `${tag} is a void element tag and must neither have \`children\` nor ` +
634
- 'use `dangerouslySetInnerHTML`.',
635
- );
636
- }
637
- // We listen to this event in case to ensure emulated bubble
638
- // listeners still fire for the error event.
639
- listenToNonDelegatedEvent('error', domElement);
640
- props = rawProps;
641
- break;
642
- case 'img':
643
- case 'link':
644
- if (
645
- rawProps.children != null ||
646
- rawProps.dangerouslySetInnerHTML != null
647
- ) {
648
- throw new Error(
649
- `${tag} is a void element tag and must neither have \`children\` nor ` +
650
- 'use `dangerouslySetInnerHTML`.',
651
- );
652
- }
653
- // eslint-disable-next-line no-fallthrough
654
- case 'image':
715
+ }
716
+ case 'image': {
717
// We listen to these events in case to ensure emulated bubble
718
// listeners still fire for error and load events.
719
listenToNonDelegatedEvent('error', domElement);
720
listenToNonDelegatedEvent('load', domElement);
659
- props = rawProps;
721
break;
661
- case 'details':
722
+ }
723
+ case 'details': {
724
// We listen to this event in case to ensure emulated bubble
725
// listeners still fire for the toggle event.
726
listenToNonDelegatedEvent('toggle', domElement);
665
- props = rawProps;
666
- break;
667
- case 'input':
668
- if (
669
- rawProps.children != null ||
670
- rawProps.dangerouslySetInnerHTML != null
671
- ) {
672
- throw new Error(
673
- `${tag} is a void element tag and must neither have \`children\` nor ` +
674
- 'use `dangerouslySetInnerHTML`.',
675
- );
676
- }
677
- ReactDOMInputInitWrapperState(domElement, rawProps);
678
- props = ReactDOMInputGetHostProps(domElement, rawProps);
679
- // We listen to this event in case to ensure emulated bubble
680
- // listeners still fire for the invalid event.
681
- listenToNonDelegatedEvent('invalid', domElement);
682
- break;
683
- case 'option':
684
- ReactDOMOptionValidateProps(domElement, rawProps);
685
- props = rawProps;
686
- break;
687
- case 'select':
688
- ReactDOMSelectInitWrapperState(domElement, rawProps);
689
- props = ReactDOMSelectGetHostProps(domElement, rawProps);
690
- // We listen to this event in case to ensure emulated bubble
691
- // listeners still fire for the invalid event.
692
- listenToNonDelegatedEvent('invalid', domElement);
693
- break;
694
- case 'textarea':
695
- ReactDOMTextareaInitWrapperState(domElement, rawProps);
696
- props = ReactDOMTextareaGetHostProps(domElement, rawProps);
697
- // We listen to this event in case to ensure emulated bubble
698
- // listeners still fire for the invalid event.
699
- listenToNonDelegatedEvent('invalid', domElement);
727
break;
728
+ }
729
+ case 'embed':
730
+ case 'source':
731
+ case 'img':
732
+ case 'link': {
733
+ // These are void elements that also need delegated events.
734
+ listenToNonDelegatedEvent('error', domElement);
735
+ listenToNonDelegatedEvent('load', domElement);
736
+ // We fallthrough to the return of the void elements
737
+ }
738
+ // eslint-disable-next-line no-fallthrough
739
case 'area':
740
case 'base':
741
case 'br':
747
case 'track':
748
case 'wbr':
749
case 'menuitem': {
712
- if (
713
- rawProps.children != null ||
714
- rawProps.dangerouslySetInnerHTML != null
715
- ) {
716
- // TODO: Can we make this a DEV warning to avoid this deny list?
717
- throw new Error(
718
- `${tag} is a void element tag and must neither have \`children\` nor ` +
719
- 'use `dangerouslySetInnerHTML`.',
720
- );
750
+ // Void elements
751
+ for (const propKey in props) {
752
+ if (!props.hasOwnProperty(propKey)) {
753
+ continue;
754
+ }
755
+ const propValue = props[propKey];
756
+ if (propValue == null) {
757
+ continue;
758
+ }
759
+ switch (propKey) {
760
+ case 'children':
761
+ case 'dangerouslySetInnerHTML': {
762
+ // TODO: Can we make this a DEV warning to avoid this deny list?
763
+ throw new Error(
764
+ `${tag} is a void element tag and must neither have \`children\` nor ` +
765
+ 'use `dangerouslySetInnerHTML`.',
766
+ );
767
+ }
768
+ // defaultChecked and defaultValue are ignored by setProp
769
+ default: {
770
+ // TODO: If the `is` prop is specified, this should go through the isCustomComponentTag flow.
771
+ setProp(domElement, tag, propKey, propValue, false, props);
772
+ }
773
+ }
774
}
775
+ return;
776
}
723
- // eslint-disable-next-line no-fallthrough
724
- default:
725
- props = rawProps;
777
}
778
728
- setInitialDOMProperties(tag, domElement, props, isCustomComponentTag);
729
-
730
- switch (tag) {
731
- case 'input':
732
- // TODO: Make sure we check if this is still unmounted or do any clean
733
- // up necessary since we never stop tracking anymore.
734
- track((domElement: any));
735
- ReactDOMInputPostMountWrapper(domElement, rawProps, false);
736
- break;
737
- case 'textarea':
738
- // TODO: Make sure we check if this is still unmounted or do any clean
739
- // up necessary since we never stop tracking anymore.
740
- track((domElement: any));
741
- ReactDOMTextareaPostMountWrapper(domElement, rawProps);
742
- break;
743
- case 'option':
744
- ReactDOMOptionPostMountWrapper(domElement, rawProps);
745
- break;
746
- case 'select':
747
- ReactDOMSelectPostMountWrapper(domElement, rawProps);
748
- break;
749
- default:
750
- if (typeof props.onClick === 'function') {
751
- // TODO: This cast may not be sound for SVG, MathML or custom elements.
752
- trapClickOnNonInteractiveElement(((domElement: any): HTMLElement));
753
- }
754
- break;
779
+ const isCustomComponentTag = isCustomComponent(tag, props);
780
+ for (const propKey in props) {
781
+ if (!props.hasOwnProperty(propKey)) {
782
+ continue;
783
+ }
784
+ const propValue = props[propKey];
785
+ if (propValue == null) {
786
+ continue;
787
+ }
788
+ setProp(domElement, tag, propKey, propValue, isCustomComponentTag, props);
789
}
790
}
791
793
export function diffProperties(
794
domElement: Element,
795
tag: string,
762
- lastRawProps: Object,
763
- nextRawProps: Object,
796
+ lastProps: Object,
797
+ nextProps: Object,
798
): null | Array<mixed> {
799
if (__DEV__) {
766
- validatePropertiesInDevelopment(tag, nextRawProps);
800
+ validatePropertiesInDevelopment(tag, nextProps);
801
}
802
803
let updatePayload: null | Array<any> = null;
804
771
- let lastProps: Object;
772
- let nextProps: Object;
773
- switch (tag) {
774
- case 'input':
775
- if (
776
- nextRawProps.children != null ||
777
- nextRawProps.dangerouslySetInnerHTML != null
778
- ) {
779
- throw new Error(
780
- `${tag} is a void element tag and must neither have \`children\` nor ` +
781
- 'use `dangerouslySetInnerHTML`.',
782
- );
783
- }
784
- lastProps = ReactDOMInputGetHostProps(domElement, lastRawProps);
785
- nextProps = ReactDOMInputGetHostProps(domElement, nextRawProps);
786
- updatePayload = [];
787
- break;
788
- case 'select':
789
- lastProps = ReactDOMSelectGetHostProps(domElement, lastRawProps);
790
- nextProps = ReactDOMSelectGetHostProps(domElement, nextRawProps);
791
- updatePayload = [];
792
- break;
793
- case 'textarea':
794
- lastProps = ReactDOMTextareaGetHostProps(domElement, lastRawProps);
795
- nextProps = ReactDOMTextareaGetHostProps(domElement, nextRawProps);
796
- updatePayload = [];
797
- break;
798
- case 'img':
799
- case 'link':
800
- case 'area':
801
- case 'base':
802
- case 'br':
803
- case 'col':
804
- case 'embed':
805
- case 'hr':
806
- case 'keygen':
807
- case 'meta':
808
- case 'param':
809
- case 'source':
810
- case 'track':
811
- case 'wbr':
812
- case 'menuitem': {
813
- if (
814
- nextRawProps.children != null ||
815
- nextRawProps.dangerouslySetInnerHTML != null
816
- ) {
817
- // TODO: Can we make this a DEV warning to avoid this deny list?
818
- throw new Error(
819
- `${tag} is a void element tag and must neither have \`children\` nor ` +
820
- 'use `dangerouslySetInnerHTML`.',
821
- );
822
- }
823
- }
824
- // eslint-disable-next-line no-fallthrough
825
- default:
826
- lastProps = lastRawProps;
827
- nextProps = nextRawProps;
828
- if (
829
- typeof lastProps.onClick !== 'function' &&
830
- typeof nextProps.onClick === 'function'
831
- ) {
832
- // TODO: This cast may not be sound for SVG, MathML or custom elements.
833
- trapClickOnNonInteractiveElement(((domElement: any): HTMLElement));
834
- }
835
- break;
836
- }
837
-
805
let propKey;
806
let styleName;
807
let styleUpdates = null;
826
}
827
break;
828
}
862
- case 'dangerouslySetInnerHTML':
863
- case 'children': {
864
- // Noop. This is handled by the clear text mechanism.
865
- break;
866
- }
867
- case 'suppressContentEditableWarning':
868
- case 'suppressHydrationWarning':
869
- case 'defaultValue': // Reserved
870
- case 'defaultChecked':
871
- case 'innerHTML': {
872
- // Noop
873
- break;
874
- }
875
- case 'autoFocus': {
876
- // Noop. It doesn't work on updates anyway.
877
- break;
878
- }
879
- case 'innerText': // Properties
880
- case 'textContent':
881
- if (enableCustomElementPropertySupport) {
882
- break;
883
- }
884
- // eslint-disable-next-line no-fallthrough
829
default: {
886
- if (registrationNameDependencies.hasOwnProperty(propKey)) {
887
- // This is a special case. If any listener updates we need to ensure
888
- // that the "current" fiber pointer gets updated so we need a commit
889
- // to update this element.
890
- if (!updatePayload) {
891
- updatePayload = [];
892
- }
893
- } else {
894
- // For all other deleted properties we add it to the queue. We use
895
- // the allowed property list in the commit phase instead.
896
- (updatePayload = updatePayload || []).push(propKey, null);
897
- }
830
+ // For all other deleted properties we add it to the queue. We use
831
+ // the allowed property list in the commit phase instead.
832
+ (updatePayload = updatePayload || []).push(propKey, null);
833
}
834
}
835
}
837
const nextProp = nextProps[propKey];
838
const lastProp = lastProps != null ? lastProps[propKey] : undefined;
839
if (
905
- !nextProps.hasOwnProperty(propKey) ||
906
- nextProp === lastProp ||
907
- (nextProp == null && lastProp == null)
840
+ nextProps.hasOwnProperty(propKey) &&
841
+ nextProp !== lastProp &&
842
+ (nextProp != null || lastProp != null)
843
) {
909
- continue;
910
- }
911
- switch (propKey) {
912
- case 'style': {
913
- if (nextProp != null && typeof nextProp !== 'object') {
914
- throw new Error(
915
- 'The `style` prop expects a mapping from style properties to values, ' +
916
- "not a string. For example, style={{marginRight: spacing + 'em'}} when " +
917
- 'using JSX.',
918
- );
919
- }
920
- if (__DEV__) {
921
- if (nextProp) {
922
- // Freeze the next style object so that we can assume it won't be
923
- // mutated. We have already warned for this in the past.
924
- Object.freeze(nextProp);
925
- }
926
- }
927
- if (lastProp) {
928
- // Unset styles on `lastProp` but not on `nextProp`.
929
- for (styleName in lastProp) {
930
- if (
931
- lastProp.hasOwnProperty(styleName) &&
932
- (!nextProp || !nextProp.hasOwnProperty(styleName))
933
- ) {
934
- if (!styleUpdates) {
935
- styleUpdates = ({}: {[string]: string});
844
+ switch (propKey) {
845
+ case 'style': {
846
+ if (lastProp) {
847
+ // Unset styles on `lastProp` but not on `nextProp`.
848
+ for (styleName in lastProp) {
849
+ if (
850
+ lastProp.hasOwnProperty(styleName) &&
851
+ (!nextProp || !nextProp.hasOwnProperty(styleName))
852
+ ) {
853
+ if (!styleUpdates) {
854
+ styleUpdates = ({}: {[string]: string});
855
+ }
856
+ styleUpdates[styleName] = '';
857
}
937
- styleUpdates[styleName] = '';
858
}
939
- }
940
- // Update styles that changed since `lastProp`.
941
- for (styleName in nextProp) {
942
- if (
943
- nextProp.hasOwnProperty(styleName) &&
944
- lastProp[styleName] !== nextProp[styleName]
945
- ) {
946
- if (!styleUpdates) {
947
- styleUpdates = ({}: {[string]: $FlowFixMe});
859
+ // Update styles that changed since `lastProp`.
860
+ for (styleName in nextProp) {
861
+ if (
862
+ nextProp.hasOwnProperty(styleName) &&
863
+ lastProp[styleName] !== nextProp[styleName]
864
+ ) {
865
+ if (!styleUpdates) {
866
+ styleUpdates = ({}: {[string]: $FlowFixMe});
867
+ }
868
+ styleUpdates[styleName] = nextProp[styleName];
869
}
949
- styleUpdates[styleName] = nextProp[styleName];
870
}
951
- }
952
- } else {
953
- // Relies on `updateStylesByID` not mutating `styleUpdates`.
954
- if (!styleUpdates) {
955
- if (!updatePayload) {
956
- updatePayload = [];
957
- }
958
- updatePayload.push(propKey, styleUpdates);
959
- }
960
- styleUpdates = nextProp;
961
- }
962
- break;
963
- }
964
- case 'dangerouslySetInnerHTML': {
965
- if (nextProp != null) {
966
- if (typeof nextProp !== 'object' || !('__html' in nextProp)) {
967
- throw new Error(
968
- '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
969
- 'Please visit https://reactjs.org/link/dangerously-set-inner-html ' +
970
- 'for more information.',
971
- );
972
- }
973
- const nextHtml = nextProp.__html;
974
- if (nextHtml != null) {
975
- if (nextProps.children != null) {
976
- throw new Error(
977
- 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
978
- );
979
- }
980
- const lastHtml = lastProp ? lastProp.__html : undefined;
981
- if (lastHtml !== nextHtml) {
982
- (updatePayload = updatePayload || []).push(propKey, nextHtml);
871
+ } else {
872
+ // Relies on `updateStylesByID` not mutating `styleUpdates`.
873
+ if (!styleUpdates) {
874
+ if (!updatePayload) {
875
+ updatePayload = [];
876
+ }
877
+ updatePayload.push(propKey, styleUpdates);
878
}
879
+ styleUpdates = nextProp;
880
}
985
- } else {
986
- // TODO: It might be too late to clear this if we have children
987
- // inserted already.
988
- }
989
- break;
990
- }
991
- case 'children': {
992
- if (typeof nextProp === 'string' || typeof nextProp === 'number') {
993
- (updatePayload = updatePayload || []).push(propKey, '' + nextProp);
994
- }
995
- break;
996
- }
997
- case 'onScroll': {
998
- if (nextProp != null) {
999
- // We eagerly listen to this even though we haven't committed yet.
1000
- if (__DEV__ && typeof nextProp !== 'function') {
1001
- warnForInvalidEventListener(propKey, nextProp);
1002
- }
1003
- listenToNonDelegatedEvent('scroll', domElement);
1004
- }
1005
- if (!updatePayload && lastProp !== nextProp) {
1006
- // This is a special case. If any listener updates we need to ensure
1007
- // that the "current" props pointer gets updated so we need a commit
1008
- // to update this element.
1009
- updatePayload = [];
1010
- }
1011
- break;
1012
- }
1013
- case 'suppressContentEditableWarning':
1014
- case 'suppressHydrationWarning':
1015
- case 'defaultValue': // Reserved
1016
- case 'defaultChecked':
1017
- case 'innerHTML': {
1018
- // Noop
1019
- break;
1020
- }
1021
- case 'autoFocus': {
1022
- // Noop on updates
1023
- break;
1024
- }
1025
- case 'innerText': // Properties
1026
- case 'textContent':
1027
- if (enableCustomElementPropertySupport) {
881
break;
882
}
1030
- // eslint-disable-next-line no-fallthrough
1031
- default: {
1032
- if (registrationNameDependencies.hasOwnProperty(propKey)) {
1033
- if (nextProp != null) {
1034
- // We eagerly listen to this even though we haven't committed yet.
1035
- if (__DEV__ && typeof nextProp !== 'function') {
1036
- warnForInvalidEventListener(propKey, nextProp);
1037
- }
1038
- }
1039
- if (!updatePayload && lastProp !== nextProp) {
1040
- // This is a special case. If any listener updates we need to ensure
1041
- // that the "current" props pointer gets updated so we need a commit
1042
- // to update this element.
1043
- updatePayload = [];
1044
- }
1045
- } else {
1046
- // For any other property we always add it to the queue and then we
1047
- // filter it out using the allowed property list during the commit.
883
+ default: {
884
(updatePayload = updatePayload || []).push(propKey, nextProp);
885
}
886
}
900
domElement: Element,
901
updatePayload: Array<any>,
902
tag: string,
1067
- lastRawProps: Object,
1068
- nextRawProps: Object,
903
+ lastProps: Object,
904
+ nextProps: Object,
905
): void {
1070
- // Update checked *before* name.
1071
- // In the middle of an update, it is possible to have multiple checked.
1072
- // When a checked radio tries to change name, browser makes another radio's checked false.
1073
- if (
1074
- tag === 'input' &&
1075
- nextRawProps.type === 'radio' &&
1076
- nextRawProps.name != null
1077
- ) {
1078
- ReactDOMInputUpdateChecked(domElement, nextRawProps);
1079
- }
1080
-
1081
- const wasCustomComponentTag = isCustomComponent(tag, lastRawProps);
1082
- const isCustomComponentTag = isCustomComponent(tag, nextRawProps);
1083
- // Apply the diff.
1084
- updateDOMProperties(
1085
- domElement,
1086
- updatePayload,
1087
- wasCustomComponentTag,
1088
- isCustomComponentTag,
1089
- );
1090
-
1091
- // TODO: Ensure that an update gets scheduled if any of the special props
1092
- // changed.
906
switch (tag) {
1094
- case 'input':
907
+ case 'input': {
908
+ // Update checked *before* name.
909
+ // In the middle of an update, it is possible to have multiple checked.
910
+ // When a checked radio tries to change name, browser makes another radio's checked false.
911
+ if (nextProps.type === 'radio' && nextProps.name != null) {
912
+ ReactDOMInputUpdateChecked(domElement, nextProps);
913
+ }
914
+ for (let i = 0; i < updatePayload.length; i += 2) {
915
+ const propKey = updatePayload[i];
916
+ const propValue = updatePayload[i + 1];
917
+ switch (propKey) {
918
+ case 'checked': {
919
+ const node = ((domElement: any): InputWithWrapperState);
920
+ const checked =
921
+ propValue != null ? propValue : node._wrapperState.initialChecked;
922
+ node.checked =
923
+ !!checked &&
924
+ typeof checked !== 'function' &&
925
+ checked !== 'symbol';
926
+ break;
927
+ }
928
+ case 'value': {
929
+ // This is handled by updateWrapper below.
930
+ break;
931
+ }
932
+ case 'children':
933
+ case 'dangerouslySetInnerHTML': {
934
+ if (propValue != null) {
935
+ throw new Error(
936
+ `${tag} is a void element tag and must neither have \`children\` nor ` +
937
+ 'use `dangerouslySetInnerHTML`.',
938
+ );
939
+ }
940
+ break;
941
+ }
942
+ // defaultChecked and defaultValue are ignored by setProp
943
+ default: {
944
+ setProp(domElement, tag, propKey, propValue, false, nextProps);
945
+ }
946
+ }
947
+ }
948
// Update the wrapper around inputs *after* updating props. This has to
1096
- // happen after `updateDOMProperties`. Otherwise HTML5 input validations
949
+ // happen after updating the rest of props. Otherwise HTML5 input validations
950
// raise warnings and prevent the new value from being assigned.
1098
- ReactDOMInputUpdateWrapper(domElement, nextRawProps);
1099
- break;
1100
- case 'textarea':
1101
- ReactDOMTextareaUpdateWrapper(domElement, nextRawProps);
1102
- break;
1103
- case 'select':
951
+ ReactDOMInputUpdateWrapper(domElement, nextProps);
952
+ return;
953
+ }
954
+ case 'select': {
955
+ for (let i = 0; i < updatePayload.length; i += 2) {
956
+ const propKey = updatePayload[i];
957
+ const propValue = updatePayload[i + 1];
958
+ switch (propKey) {
959
+ case 'value': {
960
+ // This is handled by updateWrapper below.
961
+ break;
962
+ }
963
+ // defaultValue are ignored by setProp
964
+ default: {
965
+ setProp(domElement, tag, propKey, propValue, false, nextProps);
966
+ }
967
+ }
968
+ }
969
// <select> value update needs to occur after <option> children
970
// reconciliation
1106
- ReactDOMSelectPostUpdateWrapper(domElement, nextRawProps);
1107
- break;
971
+ ReactDOMSelectPostUpdateWrapper(domElement, nextProps);
972
+ return;
973
+ }
974
+ case 'textarea': {
975
+ for (let i = 0; i < updatePayload.length; i += 2) {
976
+ const propKey = updatePayload[i];
977
+ const propValue = updatePayload[i + 1];
978
+ switch (propKey) {
979
+ case 'value': {
980
+ // This is handled by updateWrapper below.
981
+ break;
982
+ }
983
+ case 'children': {
984
+ // TODO: This doesn't actually do anything if it updates.
985
+ break;
986
+ }
987
+ case 'dangerouslySetInnerHTML': {
988
+ if (propValue != null) {
989
+ // TODO: Do we really need a special error message for this. It's also pretty blunt.
990
+ throw new Error(
991
+ '`dangerouslySetInnerHTML` does not make sense on <textarea>.',
992
+ );
993
+ }
994
+ break;
995
+ }
996
+ // defaultValue is ignored by setProp
997
+ default: {
998
+ setProp(domElement, tag, propKey, propValue, false, nextProps);
999
+ }
1000
+ }
1001
+ }
1002
+ ReactDOMTextareaUpdateWrapper(domElement, nextProps);
1003
+ return;
1004
+ }
1005
+ case 'img':
1006
+ case 'link':
1007
+ case 'area':
1008
+ case 'base':
1009
+ case 'br':
1010
+ case 'col':
1011
+ case 'embed':
1012
+ case 'hr':
1013
+ case 'keygen':
1014
+ case 'meta':
1015
+ case 'param':
1016
+ case 'source':
1017
+ case 'track':
1018
+ case 'wbr':
1019
+ case 'menuitem': {
1020
+ // Void elements
1021
+ for (let i = 0; i < updatePayload.length; i += 2) {
1022
+ const propKey = updatePayload[i];
1023
+ const propValue = updatePayload[i + 1];
1024
+ switch (propKey) {
1025
+ case 'children':
1026
+ case 'dangerouslySetInnerHTML': {
1027
+ if (propValue != null) {
1028
+ // TODO: Can we make this a DEV warning to avoid this deny list?
1029
+ throw new Error(
1030
+ `${tag} is a void element tag and must neither have \`children\` nor ` +
1031
+ 'use `dangerouslySetInnerHTML`.',
1032
+ );
1033
+ }
1034
+ break;
1035
+ }
1036
+ // defaultChecked and defaultValue are ignored by setProp
1037
+ default: {
1038
+ // TODO: If the `is` prop is specified, this should go through the isCustomComponentTag flow.
1039
+ setProp(domElement, tag, propKey, propValue, false, nextProps);
1040
+ }
1041
+ }
1042
+ }
1043
+ return;
1044
+ }
1045
+ }
1046
+
1047
+ // TODO: Handle wasCustomComponentTag. Changing "is" isn't valid.
1048
+ // const wasCustomComponentTag = isCustomComponent(tag, lastProps);
1049
+ const isCustomComponentTag = isCustomComponent(tag, nextProps);
1050
+ // Apply the diff.
1051
+ for (let i = 0; i < updatePayload.length; i += 2) {
1052
+ const propKey = updatePayload[i];
1053
+ const propValue = updatePayload[i + 1];
1054
+ setProp(
1055
+ domElement,
1056
+ tag,
1057
+ propKey,
1058
+ propValue,
1059
+ isCustomComponentTag,
1060
+ nextProps,
1061
+ );
1062
}
1063
}
1064
1093
function diffHydratedCustomComponent(
1094
domElement: Element,
1095
tag: string,
1142
- rawProps: Object,
1096
+ props: Object,
1097
parentNamespaceDev: string,
1098
extraAttributeNames: Set<string>,
1099
) {
1146
- for (const propKey in rawProps) {
1147
- if (!rawProps.hasOwnProperty(propKey)) {
1100
+ for (const propKey in props) {
1101
+ if (!props.hasOwnProperty(propKey)) {
1102
continue;
1103
}
1150
- const nextProp = rawProps[propKey];
1104
+ const nextProp = props[propKey];
1105
if (nextProp == null) {
1106
continue;
1107
}
1111
}
1112
continue;
1113
}
1160
- if (rawProps.suppressHydrationWarning === true) {
1114
+ if (props.suppressHydrationWarning === true) {
1115
// Don't bother comparing. We're ignoring all these warnings.
1116
continue;
1117
}
1184
function diffHydratedGenericElement(
1185
domElement: Element,
1186
tag: string,
1233
- rawProps: Object,
1187
+ props: Object,
1188
parentNamespaceDev: string,
1189
extraAttributeNames: Set<string>,
1190
) {
1237
- for (const propKey in rawProps) {
1238
- if (!rawProps.hasOwnProperty(propKey)) {
1191
+ for (const propKey in props) {
1192
+ if (!props.hasOwnProperty(propKey)) {
1193
continue;
1194
}
1241
- const nextProp = rawProps[propKey];
1195
+ const nextProp = props[propKey];
1196
if (nextProp == null) {
1197
continue;
1198
}
1202
}
1203
continue;
1204
}
1251
- if (rawProps.suppressHydrationWarning === true) {
1205
+ if (props.suppressHydrationWarning === true) {
1206
// Don't bother comparing. We're ignoring all these warnings.
1207
continue;
1208
}
1288
export function diffHydratedProperties(
1289
domElement: Element,
1290
tag: string,
1337
- rawProps: Object,
1291
+ props: Object,
1292
isConcurrentMode: boolean,
1293
shouldWarnDev: boolean,
1294
parentNamespaceDev: string,
1295
): null | Array<mixed> {
1296
if (__DEV__) {
1343
- validatePropertiesInDevelopment(tag, rawProps);
1297
+ validatePropertiesInDevelopment(tag, props);
1298
}
1299
1300
// TODO: Make sure that we check isMounted before firing any of these events.
1337
listenToNonDelegatedEvent('toggle', domElement);
1338
break;
1339
case 'input':
1386
- ReactDOMInputInitWrapperState(domElement, rawProps);
1340
+ ReactDOMInputInitWrapperState(domElement, props);
1341
// We listen to this event in case to ensure emulated bubble
1342
// listeners still fire for the invalid event.
1343
listenToNonDelegatedEvent('invalid', domElement);
1344
+ // TODO: Make sure we check if this is still unmounted or do any clean
1345
+ // up necessary since we never stop tracking anymore.
1346
+ track((domElement: any));
1347
+ // For input and textarea we current always set the value property at
1348
+ // post mount to force it to diverge from attributes. However, for
1349
+ // option and select we don't quite do the same thing and select
1350
+ // is not resilient to the DOM state changing so we don't do that here.
1351
+ // TODO: Consider not doing this for input and textarea.
1352
+ ReactDOMInputPostMountWrapper(domElement, props, true);
1353
break;
1354
case 'option':
1392
- ReactDOMOptionValidateProps(domElement, rawProps);
1355
+ ReactDOMOptionValidateProps(domElement, props);
1356
break;
1357
case 'select':
1395
- ReactDOMSelectInitWrapperState(domElement, rawProps);
1358
+ ReactDOMSelectInitWrapperState(domElement, props);
1359
// We listen to this event in case to ensure emulated bubble
1360
// listeners still fire for the invalid event.
1361
listenToNonDelegatedEvent('invalid', domElement);
1362
break;
1363
case 'textarea':
1401
- ReactDOMTextareaInitWrapperState(domElement, rawProps);
1364
+ ReactDOMTextareaInitWrapperState(domElement, props);
1365
// We listen to this event in case to ensure emulated bubble
1366
// listeners still fire for the invalid event.
1367
listenToNonDelegatedEvent('invalid', domElement);
1368
+ // TODO: Make sure we check if this is still unmounted or do any clean
1369
+ // up necessary since we never stop tracking anymore.
1370
+ track((domElement: any));
1371
+ ReactDOMTextareaPostMountWrapper(domElement, props);
1372
break;
1373
}
1374
1408
- if (rawProps.hasOwnProperty('onScroll')) {
1409
- listenToNonDelegatedEvent('scroll', domElement);
1410
- }
1411
-
1375
let updatePayload = null;
1376
1414
- const children = rawProps.children;
1377
+ const children = props.children;
1378
// For text content children we compare against textContent. This
1379
// might match additional HTML that is hidden when we read it using
1380
// textContent. E.g. "foo" will match "f<span>oo</span>" but that still
1386
// TODO: Should we use domElement.firstChild.nodeValue to compare?
1387
if (typeof children === 'string' || typeof children === 'number') {
1388
if (domElement.textContent !== '' + children) {
1426
- if (rawProps.suppressHydrationWarning !== true) {
1389
+ if (props.suppressHydrationWarning !== true) {
1390
checkForUnmatchedText(
1391
domElement.textContent,
1392
children,
1400
}
1401
}
1402
1403
+ if (props.onScroll != null) {
1404
+ listenToNonDelegatedEvent('scroll', domElement);
1405
+ }
1406
+
1407
+ if (props.onClick != null) {
1408
+ // TODO: This cast may not be sound for SVG, MathML or custom elements.
1409
+ trapClickOnNonInteractiveElement(((domElement: any): HTMLElement));
1410
+ }
1411
+
1412
if (__DEV__ && shouldWarnDev) {
1413
const extraAttributeNames: Set<string> = new Set();
1414
const attributes = domElement.attributes;
1429
extraAttributeNames.add(attributes[i].name);
1430
}
1431
}
1460
- if (isCustomComponent(tag, rawProps)) {
1432
+ if (isCustomComponent(tag, props)) {
1433
diffHydratedCustomComponent(
1434
domElement,
1435
tag,
1464
- rawProps,
1436
+ props,
1437
parentNamespaceDev,
1438
extraAttributeNames,
1439
);
1441
diffHydratedGenericElement(
1442
domElement,
1443
tag,
1472
- rawProps,
1444
+ props,
1445
parentNamespaceDev,
1446
extraAttributeNames,
1447
);
1448
}
1449
if (
1450
extraAttributeNames.size > 0 &&
1479
- rawProps.suppressHydrationWarning !== true
1451
+ props.suppressHydrationWarning !== true
1452
) {
1453
warnForExtraAttributes(extraAttributeNames);
1454
}
1455
}
1456
1485
- switch (tag) {
1486
- case 'input':
1487
- // TODO: Make sure we check if this is still unmounted or do any clean
1488
- // up necessary since we never stop tracking anymore.
1489
- track((domElement: any));
1490
- ReactDOMInputPostMountWrapper(domElement, rawProps, true);
1491
- break;
1492
- case 'textarea':
1493
- // TODO: Make sure we check if this is still unmounted or do any clean
1494
- // up necessary since we never stop tracking anymore.
1495
- track((domElement: any));
1496
- ReactDOMTextareaPostMountWrapper(domElement, rawProps);
1497
- break;
1498
- case 'select':
1499
- case 'option':
1500
- // For input and textarea we current always set the value property at
1501
- // post mount to force it to diverge from attributes. However, for
1502
- // option and select we don't quite do the same thing and select
1503
- // is not resilient to the DOM state changing so we don't do that here.
1504
- // TODO: Consider not doing this for input and textarea.
1505
- break;
1506
- default:
1507
- if (typeof rawProps.onClick === 'function') {
1508
- // TODO: This cast may not be sound for SVG, MathML or custom elements.
1509
- trapClickOnNonInteractiveElement(((domElement: any): HTMLElement));
1510
- }
1511
- break;
1512
- }
1513
-
1457
return updatePayload;
1458
}
1459