@samitouri / QOS-React-2 / commits / 4131af3e4b

Ignore SSR warning using explicit suppressHydrationWarning option (#11126)

* Pass parent type and props to insert/delete hydration warning hooks For this to work, we need to split the API into a container and normal version. Since the root doesn't have a type nor props. * Ignore SSR warning using explicit suppressHydrationWarning option This lets you ignore the warning on a single element and its direct child content. This is useful for simple fields that you're expecting to fail such as time stamps. Note that this still won't patch up such content so it'll remain inconsistent. It's also not suitable for nested complex content that may change. * Suppress warning of inserted/deleted direct children * Add fixture testing hydration warning Also fixing the render->hydrate API change in the fixture * Add hooks when text hydration doesn't match up The purpose of these hooks is to pass the parent context to them. I don't want to do that in the normal hydrateTextInstance hooks since this is only used in DEV. This is also in line with what happens if there is no text instance at all and we invoke didNotFindHydratableTextInstance. * Move mismatch text hydration warning to the new hooks This lets us ignore this call when we have parent props available and the suppression flag is set.

Sebastian Markbåge committed Oct 6, 2017 at 19:50 UTC 4131af3e4bf52f3a003537ec95a1655147c81270
11 files changed +352 -48
fixtures/ssr/src/components/Page.js
+3
@@ -15,6 +15,9 @@ export default class Page extends Component {
15 );
16 return (
17 <div>
18 + <p suppressHydrationWarning={true}>
19 + A random number: {Math.random()}
20 + </p>
21 <p>
22 {!this.state.active ? link : 'Thanks!'}
23 </p>
fixtures/ssr/src/index.js
+2 -2
@@ -1,6 +1,6 @@
1 import React from 'react';
2 -import {render} from 'react-dom';
2 +import {hydrate} from 'react-dom';
3
4 import App from './components/App';
5
6 -render(<App assets={window.assetManifest} />, document);
6 +hydrate(<App assets={window.assetManifest} />, document);
src/renderers/dom/fiber/ReactDOMFiberComponent.js
+27 -11
@@ -53,6 +53,7 @@ var registrationNameModules = EventPluginRegistry.registrationNameModules;
53
54 var DANGEROUSLY_SET_INNER_HTML = 'dangerouslySetInnerHTML';
55 var SUPPRESS_CONTENT_EDITABLE_WARNING = 'suppressContentEditableWarning';
56 +var SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';
57 var CHILDREN = 'children';
58 var STYLE = 'style';
59 var HTML = '__html';
@@ -273,7 +274,10 @@ function setInitialDOMProperties(
274 } else if (typeof nextProp === 'number') {
275 setTextContent(domElement, '' + nextProp);
276 }
276 - } else if (propKey === SUPPRESS_CONTENT_EDITABLE_WARNING) {
277 + } else if (
278 + propKey === SUPPRESS_CONTENT_EDITABLE_WARNING ||
279 + propKey === SUPPRESS_HYDRATION_WARNING
280 + ) {
281 // Noop
282 } else if (registrationNameModules.hasOwnProperty(propKey)) {
283 if (nextProp != null) {
@@ -664,7 +668,10 @@ var ReactDOMFiberComponent = {
668 propKey === CHILDREN
669 ) {
670 // Noop. This is handled by the clear text mechanism.
667 - } else if (propKey === SUPPRESS_CONTENT_EDITABLE_WARNING) {
671 + } else if (
672 + propKey === SUPPRESS_CONTENT_EDITABLE_WARNING ||
673 + propKey === SUPPRESS_HYDRATION_WARNING
674 + ) {
675 // Noop
676 } else if (registrationNameModules.hasOwnProperty(propKey)) {
677 // This is a special case. If any listener updates we need to ensure
@@ -750,7 +757,10 @@ var ReactDOMFiberComponent = {
757 ) {
758 (updatePayload = updatePayload || []).push(propKey, '' + nextProp);
759 }
753 - } else if (propKey === SUPPRESS_CONTENT_EDITABLE_WARNING) {
760 + } else if (
761 + propKey === SUPPRESS_CONTENT_EDITABLE_WARNING ||
762 + propKey === SUPPRESS_HYDRATION_WARNING
763 + ) {
764 // Noop
765 } else if (registrationNameModules.hasOwnProperty(propKey)) {
766 if (nextProp != null) {
@@ -828,6 +838,8 @@ var ReactDOMFiberComponent = {
838 rootContainerElement: Element | Document,
839 ): null | Array<mixed> {
840 if (__DEV__) {
841 + var suppressHydrationWarning =
842 + rawProps[SUPPRESS_HYDRATION_WARNING] === true;
843 var isCustomComponentTag = isCustomComponent(tag, rawProps);
844 validatePropertiesInDevelopment(tag, rawProps);
845 if (isCustomComponentTag && !didWarnShadyDOM && domElement.shadyRoot) {
@@ -986,14 +998,14 @@ var ReactDOMFiberComponent = {
998 // TODO: Should we use domElement.firstChild.nodeValue to compare?
999 if (typeof nextProp === 'string') {
1000 if (domElement.textContent !== nextProp) {
989 - if (__DEV__) {
1001 + if (__DEV__ && !suppressHydrationWarning) {
1002 warnForTextDifference(domElement.textContent, nextProp);
1003 }
1004 updatePayload = [CHILDREN, nextProp];
1005 }
1006 } else if (typeof nextProp === 'number') {
1007 if (domElement.textContent !== '' + nextProp) {
996 - if (__DEV__) {
1008 + if (__DEV__ && !suppressHydrationWarning) {
1009 warnForTextDifference(domElement.textContent, nextProp);
1010 }
1011 updatePayload = [CHILDREN, '' + nextProp];
@@ -1010,8 +1022,11 @@ var ReactDOMFiberComponent = {
1022 // Validate that the properties correspond to their expected values.
1023 var serverValue;
1024 var propertyInfo;
1013 - if (
1025 + if (suppressHydrationWarning) {
1026 + // Don't bother comparing. We're ignoring all these warnings.
1027 + } else if (
1028 propKey === SUPPRESS_CONTENT_EDITABLE_WARNING ||
1029 + propKey === SUPPRESS_HYDRATION_WARNING ||
1030 // Controlled attributes are not validated
1031 // TODO: Only ignore them on controlled tags.
1032 propKey === 'value' ||
@@ -1085,7 +1100,7 @@ var ReactDOMFiberComponent = {
1100
1101 if (__DEV__) {
1102 // $FlowFixMe - Should be inferred as not undefined.
1088 - if (extraAttributeNames.size > 0) {
1103 + if (extraAttributeNames.size > 0 && !suppressHydrationWarning) {
1104 // $FlowFixMe - Should be inferred as not undefined.
1105 warnForExtraAttributes(extraAttributeNames);
1106 }
@@ -1125,12 +1140,13 @@ var ReactDOMFiberComponent = {
1140
1141 diffHydratedText(textNode: Text, text: string): boolean {
1142 const isDifferent = textNode.nodeValue !== text;
1143 + return isDifferent;
1144 + },
1145 +
1146 + warnForUnmatchedText(textNode: Text, text: string) {
1147 if (__DEV__) {
1129 - if (isDifferent) {
1130 - warnForTextDifference(textNode.nodeValue, text);
1131 - }
1148 + warnForTextDifference(textNode.nodeValue, text);
1149 }
1133 - return isDifferent;
1150 },
1151
1152 warnForDeletedHydratableElement(
src/renderers/dom/fiber/ReactDOMFiberEntry.js
+78 -9
@@ -50,6 +50,7 @@ var {
50 updateProperties,
51 diffHydratedProperties,
52 diffHydratedText,
53 + warnForUnmatchedText,
54 warnForDeletedHydratableElement,
55 warnForDeletedHydratableText,
56 warnForInsertedHydratedElement,
@@ -58,6 +59,7 @@ var {
59 var {precacheFiberNode, updateFiberProps} = ReactDOMComponentTree;
60
61 if (__DEV__) {
62 + var SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';
63 var lowPriorityWarning = require('lowPriorityWarning');
64 var warning = require('fbjs/lib/warning');
65 var validateDOMNesting = require('validateDOMNesting');
@@ -99,6 +101,7 @@ type Props = {
101 autoFocus?: boolean,
102 children?: mixed,
103 hidden?: boolean,
104 + suppressHydrationWarning?: boolean,
105 };
106 type Instance = Element;
107 type TextInstance = Text;
@@ -523,30 +526,96 @@ var DOMRenderer = ReactFiberReconciler({
526 return diffHydratedText(textInstance, text);
527 },
528
529 + didNotMatchHydratedContainerTextInstance(
530 + parentContainer: Container,
531 + textInstance: TextInstance,
532 + text: string,
533 + ) {
534 + if (__DEV__) {
535 + warnForUnmatchedText(textInstance, text);
536 + }
537 + },
538 +
539 + didNotMatchHydratedTextInstance(
540 + parentType: string,
541 + parentProps: Props,
542 + parentInstance: Instance,
543 + textInstance: TextInstance,
544 + text: string,
545 + ) {
546 + if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
547 + warnForUnmatchedText(textInstance, text);
548 + }
549 + },
550 +
551 + didNotHydrateContainerInstance(
552 + parentContainer: Container,
553 + instance: Instance | TextInstance,
554 + ) {
555 + if (__DEV__) {
556 + if (instance.nodeType === 1) {
557 + warnForDeletedHydratableElement(parentContainer, (instance: any));
558 + } else {
559 + warnForDeletedHydratableText(parentContainer, (instance: any));
560 + }
561 + }
562 + },
563 +
564 didNotHydrateInstance(
527 - parentInstance: Instance | Container,
565 + parentType: string,
566 + parentProps: Props,
567 + parentInstance: Instance,
568 instance: Instance | TextInstance,
569 ) {
530 - if (instance.nodeType === 1) {
531 - warnForDeletedHydratableElement(parentInstance, (instance: any));
532 - } else {
533 - warnForDeletedHydratableText(parentInstance, (instance: any));
570 + if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
571 + if (instance.nodeType === 1) {
572 + warnForDeletedHydratableElement(parentInstance, (instance: any));
573 + } else {
574 + warnForDeletedHydratableText(parentInstance, (instance: any));
575 + }
576 + }
577 + },
578 +
579 + didNotFindHydratableContainerInstance(
580 + parentContainer: Container,
581 + type: string,
582 + props: Props,
583 + ) {
584 + if (__DEV__) {
585 + warnForInsertedHydratedElement(parentContainer, type, props);
586 + }
587 + },
588 +
589 + didNotFindHydratableContainerTextInstance(
590 + parentContainer: Container,
591 + text: string,
592 + ) {
593 + if (__DEV__) {
594 + warnForInsertedHydratedText(parentContainer, text);
595 }
596 },
597
598 didNotFindHydratableInstance(
538 - parentInstance: Instance | Container,
599 + parentType: string,
600 + parentProps: Props,
601 + parentInstance: Instance,
602 type: string,
603 props: Props,
604 ) {
542 - warnForInsertedHydratedElement(parentInstance, type, props);
605 + if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
606 + warnForInsertedHydratedElement(parentInstance, type, props);
607 + }
608 },
609
610 didNotFindHydratableTextInstance(
546 - parentInstance: Instance | Container,
611 + parentType: string,
612 + parentProps: Props,
613 + parentInstance: Instance,
614 text: string,
615 ) {
549 - warnForInsertedHydratedText(parentInstance, text);
616 + if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
617 + warnForInsertedHydratedText(parentInstance, text);
618 + }
619 },
620
621 scheduleDeferredCallback: ReactDOMFrameScheduling.rIC,
src/renderers/dom/shared/DOMProperty.js
+1
@@ -21,6 +21,7 @@ var RESERVED_PROPS = {
21 defaultChecked: true,
22 innerHTML: true,
23 suppressContentEditableWarning: true,
24 + suppressHydrationWarning: true,
25 style: true,
26 };
27
src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
+8
@@ -318,6 +318,7 @@ describe('ReactDOMComponent', () => {
318 <my-component
319 children={['foo']}
320 suppressContentEditableWarning={true}
321 + suppressHydrationWarning={true}
322 />,
323 container,
324 );
@@ -325,11 +326,15 @@ describe('ReactDOMComponent', () => {
326 expect(
327 container.firstChild.hasAttribute('suppressContentEditableWarning'),
328 ).toBe(false);
329 + expect(
330 + container.firstChild.hasAttribute('suppressHydrationWarning'),
331 + ).toBe(false);
332
333 ReactDOM.render(
334 <my-component
335 children={['bar']}
336 suppressContentEditableWarning={false}
337 + suppressHydrationWarning={false}
338 />,
339 container,
340 );
@@ -337,6 +342,9 @@ describe('ReactDOMComponent', () => {
342 expect(
343 container.firstChild.hasAttribute('suppressContentEditableWarning'),
344 ).toBe(false);
345 + expect(
346 + container.firstChild.hasAttribute('suppressHydrationWarning'),
347 + ).toBe(false);
348 });
349
350 it('should skip dangerouslySetInnerHTML on web components', () => {
src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+97
@@ -725,6 +725,16 @@ describe('ReactDOMServerIntegration', () => {
725 );
726 expect(e.getAttribute('dangerouslySetInnerHTML')).toBe(null);
727 });
728 +
729 + itRenders('no suppressContentEditableWarning attribute', async render => {
730 + const e = await render(<div suppressContentEditableWarning={true} />);
731 + expect(e.getAttribute('suppressContentEditableWarning')).toBe(null);
732 + });
733 +
734 + itRenders('no suppressHydrationWarning attribute', async render => {
735 + const e = await render(<span suppressHydrationWarning={true} />);
736 + expect(e.getAttribute('suppressHydrationWarning')).toBe(null);
737 + });
738 });
739
740 describe('inline styles', function() {
@@ -2623,6 +2633,36 @@ describe('ReactDOMServerIntegration', () => {
2633
2634 it('should error reconnecting different attribute values', () =>
2635 expectMarkupMismatch(<div id="foo" />, <div id="bar" />));
2636 +
2637 + it('can explicitly ignore errors reconnecting different element types of children', () =>
2638 + expectMarkupMatch(
2639 + <div><div /></div>,
2640 + <div suppressHydrationWarning={true}><span /></div>,
2641 + ));
2642 +
2643 + it('can explicitly ignore errors reconnecting missing attributes', () =>
2644 + expectMarkupMatch(
2645 + <div id="foo" />,
2646 + <div suppressHydrationWarning={true} />,
2647 + ));
2648 +
2649 + it('can explicitly ignore errors reconnecting added attributes', () =>
2650 + expectMarkupMatch(
2651 + <div />,
2652 + <div id="foo" suppressHydrationWarning={true} />,
2653 + ));
2654 +
2655 + it('can explicitly ignore errors reconnecting different attribute values', () =>
2656 + expectMarkupMatch(
2657 + <div id="foo" />,
2658 + <div id="bar" suppressHydrationWarning={true} />,
2659 + ));
2660 +
2661 + it('can not deeply ignore errors reconnecting different attribute values', () =>
2662 + expectMarkupMismatch(
2663 + <div><div id="foo" /></div>,
2664 + <div suppressHydrationWarning={true}><div id="bar" /></div>,
2665 + ));
2666 });
2667
2668 describe('inline styles', function() {
@@ -2661,6 +2701,18 @@ describe('ReactDOMServerIntegration', () => {
2701 <div style={{width: '1px', fontSize: '2px'}} />,
2702 <div style={{fontSize: '2px', width: '1px'}} />,
2703 ));
2704 +
2705 + it('can explicitly ignore errors reconnecting added style values', () =>
2706 + expectMarkupMatch(
2707 + <div style={{}} />,
2708 + <div style={{width: '1px'}} suppressHydrationWarning={true} />,
2709 + ));
2710 +
2711 + it('can explicitly ignore reconnecting different style values', () =>
2712 + expectMarkupMatch(
2713 + <div style={{width: '1px'}} />,
2714 + <div style={{width: '2px'}} suppressHydrationWarning={true} />,
2715 + ));
2716 });
2717
2718 describe('text nodes', function() {
@@ -2681,6 +2733,18 @@ describe('ReactDOMServerIntegration', () => {
2733 <div>{'Text1'}{'Text2'}</div>,
2734 <div>{'Text1'}{'Text3'}</div>,
2735 ));
2736 +
2737 + it('can explicitly ignore reconnecting different text', () =>
2738 + expectMarkupMatch(
2739 + <div>Text</div>,
2740 + <div suppressHydrationWarning={true}>Other Text</div>,
2741 + ));
2742 +
2743 + it('can explicitly ignore reconnecting different text in two code blocks', () =>
2744 + expectMarkupMatch(
2745 + <div suppressHydrationWarning={true}>{'Text1'}{'Text2'}</div>,
2746 + <div suppressHydrationWarning={true}>{'Text1'}{'Text3'}</div>,
2747 + ));
2748 });
2749
2750 describe('element trees and children', function() {
@@ -2731,6 +2795,30 @@ describe('ReactDOMServerIntegration', () => {
2795
2796 it('can distinguish an empty component from an empty text component', () =>
2797 expectMarkupMatch(<div><EmptyComponent /></div>, <div>{''}</div>));
2798 +
2799 + it('can explicitly ignore reconnecting more children', () =>
2800 + expectMarkupMatch(
2801 + <div><div /></div>,
2802 + <div suppressHydrationWarning={true}><div /><div /></div>,
2803 + ));
2804 +
2805 + it('can explicitly ignore reconnecting fewer children', () =>
2806 + expectMarkupMatch(
2807 + <div><div /><div /></div>,
2808 + <div suppressHydrationWarning={true}><div /></div>,
2809 + ));
2810 +
2811 + it('can explicitly ignore reconnecting reordered children', () =>
2812 + expectMarkupMatch(
2813 + <div suppressHydrationWarning={true}><div /><span /></div>,
2814 + <div suppressHydrationWarning={true}><span /><div /></div>,
2815 + ));
2816 +
2817 + it('can not deeply ignore reconnecting reordered children', () =>
2818 + expectMarkupMismatch(
2819 + <div suppressHydrationWarning={true}><div><div /><span /></div></div>,
2820 + <div suppressHydrationWarning={true}><div><span /><div /></div></div>,
2821 + ));
2822 });
2823
2824 // Markup Mismatches: misc
@@ -2739,5 +2827,14 @@ describe('ReactDOMServerIntegration', () => {
2827 <div dangerouslySetInnerHTML={{__html: "<span id='child1'/>"}} />,
2828 <div dangerouslySetInnerHTML={{__html: "<span id='child2'/>"}} />,
2829 ));
2830 +
2831 + it('can explicitly ignore reconnecting a div with different dangerouslySetInnerHTML', () =>
2832 + expectMarkupMatch(
2833 + <div dangerouslySetInnerHTML={{__html: "<span id='child1'/>"}} />,
2834 + <div
2835 + dangerouslySetInnerHTML={{__html: "<span id='child2'/>"}}
2836 + suppressHydrationWarning={true}
2837 + />,
2838 + ));
2839 });
2840 });
src/renderers/dom/shared/hooks/possibleStandardNames.js
+1
@@ -401,6 +401,7 @@ var possibleStandardNames = {
401 strokeopacity: 'strokeOpacity',
402 'stroke-opacity': 'strokeOpacity',
403 suppresscontenteditablewarning: 'suppressContentEditableWarning',
404 + suppresshydrationwarning: 'suppressHydrationWarning',
405 surfacescale: 'surfaceScale',
406 systemlanguage: 'systemLanguage',
407 tablevalues: 'tableValues',
src/renderers/shared/fiber/ReactFiberHydrationContext.js
+97 -23
@@ -44,7 +44,13 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
44 getFirstHydratableChild,
45 hydrateInstance,
46 hydrateTextInstance,
47 + didNotMatchHydratedContainerTextInstance,
48 + didNotMatchHydratedTextInstance,
49 + didNotHydrateContainerInstance,
50 didNotHydrateInstance,
51 + // TODO: These are currently unused, see below.
52 + // didNotFindHydratableContainerInstance,
53 + // didNotFindHydratableContainerTextInstance,
54 didNotFindHydratableInstance,
55 didNotFindHydratableTextInstance,
56 } = config;
@@ -57,7 +63,12 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
63 getFirstHydratableChild &&
64 hydrateInstance &&
65 hydrateTextInstance &&
66 + didNotMatchHydratedContainerTextInstance &&
67 + didNotMatchHydratedTextInstance &&
68 + didNotHydrateContainerInstance &&
69 didNotHydrateInstance &&
70 + // didNotFindHydratableContainerInstance &&
71 + // didNotFindHydratableContainerTextInstance &&
72 didNotFindHydratableInstance &&
73 didNotFindHydratableTextInstance)
74 ) {
@@ -105,10 +116,18 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
116 if (__DEV__) {
117 switch (returnFiber.tag) {
118 case HostRoot:
108 - didNotHydrateInstance(returnFiber.stateNode.containerInfo, instance);
119 + didNotHydrateContainerInstance(
120 + returnFiber.stateNode.containerInfo,
121 + instance,
122 + );
123 break;
124 case HostComponent:
111 - didNotHydrateInstance(returnFiber.stateNode, instance);
125 + didNotHydrateInstance(
126 + returnFiber.type,
127 + returnFiber.memoizedProps,
128 + returnFiber.stateNode,
129 + instance,
130 + );
131 break;
132 }
133 }
@@ -134,32 +153,57 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
153 function insertNonHydratedInstance(returnFiber: Fiber, fiber: Fiber) {
154 fiber.effectTag |= Placement;
155 if (__DEV__) {
137 - var parentInstance;
156 switch (returnFiber.tag) {
157 // TODO: Currently we don't warn for insertions into the root because
158 // we always insert into the root in the non-hydrating case. We just
159 // delete the existing content. Reenable this once we have a better
160 // strategy for determining if we're hydrating or not.
143 - // case HostRoot:
144 - // parentInstance = returnFiber.stateNode.containerInfo;
161 + // case HostRoot: {
162 + // const parentContainer = returnFiber.stateNode.containerInfo;
163 + // switch (fiber.tag) {
164 + // case HostComponent:
165 + // const type = fiber.type;
166 + // const props = fiber.pendingProps;
167 + // didNotFindHydratableContainerInstance(parentContainer, type, props);
168 + // break;
169 + // case HostText:
170 + // const text = fiber.pendingProps;
171 + // didNotFindHydratableContainerTextInstance(parentContainer, text);
172 + // break;
173 + // }
174 // break;
146 - case HostComponent:
147 - parentInstance = returnFiber.stateNode;
175 + // }
176 + case HostComponent: {
177 + const parentType = returnFiber.type;
178 + const parentProps = returnFiber.memoizedProps;
179 + const parentInstance = returnFiber.stateNode;
180 + switch (fiber.tag) {
181 + case HostComponent:
182 + const type = fiber.type;
183 + const props = fiber.pendingProps;
184 + didNotFindHydratableInstance(
185 + parentType,
186 + parentProps,
187 + parentInstance,
188 + type,
189 + props,
190 + );
191 + break;
192 + case HostText:
193 + const text = fiber.pendingProps;
194 + didNotFindHydratableTextInstance(
195 + parentType,
196 + parentProps,
197 + parentInstance,
198 + text,
199 + );
200 + break;
201 + }
202 break;
203 + }
204 default:
205 return;
206 }
152 - switch (fiber.tag) {
153 - case HostComponent:
154 - const type = fiber.type;
155 - const props = fiber.pendingProps;
156 - didNotFindHydratableInstance(parentInstance, type, props);
157 - break;
158 - case HostText:
159 - const text = fiber.pendingProps;
160 - didNotFindHydratableTextInstance(parentInstance, text);
161 - break;
162 - }
207 }
208 }
209
@@ -243,11 +287,41 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
287
288 function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
289 const textInstance: TI = fiber.stateNode;
246 - const shouldUpdate = hydrateTextInstance(
247 - textInstance,
248 - fiber.memoizedProps,
249 - fiber,
250 - );
290 + const textContent: string = fiber.memoizedProps;
291 + const shouldUpdate = hydrateTextInstance(textInstance, textContent, fiber);
292 + if (__DEV__) {
293 + if (shouldUpdate) {
294 + // We assume that prepareToHydrateHostTextInstance is called in a context where the
295 + // hydration parent is the parent host component of this host text.
296 + const returnFiber = hydrationParentFiber;
297 + if (returnFiber !== null) {
298 + switch (returnFiber.tag) {
299 + case HostRoot: {
300 + const parentContainer = returnFiber.stateNode.containerInfo;
301 + didNotMatchHydratedContainerTextInstance(
302 + parentContainer,
303 + textInstance,
304 + textContent,
305 + );
306 + break;
307 + }
308 + case HostComponent: {
309 + const parentType = returnFiber.type;
310 + const parentProps = returnFiber.memoizedProps;
311 + const parentInstance = returnFiber.stateNode;
312 + didNotMatchHydratedTextInstance(
313 + parentType,
314 + parentProps,
315 + parentInstance,
316 + textInstance,
317 + textContent,
318 + );
319 + break;
320 + }
321 + }
322 + }
323 + }
324 + }
325 return shouldUpdate;
326 }
327
src/renderers/shared/fiber/ReactFiberReconciler.js
+37 -3
@@ -140,14 +140,48 @@ export type HostConfig<T, P, I, TI, PI, C, CX, PL> = {
140 text: string,
141 internalInstanceHandle: OpaqueHandle,
142 ) => boolean,
143 - didNotHydrateInstance?: (parentInstance: I | C, instance: I | TI) => void,
143 + didNotMatchHydratedContainerTextInstance?: (
144 + parentContainer: C,
145 + textInstance: TI,
146 + text: string,
147 + ) => void,
148 + didNotMatchHydratedTextInstance?: (
149 + parentType: T,
150 + parentProps: P,
151 + parentInstance: I,
152 + textInstance: TI,
153 + text: string,
154 + ) => void,
155 + didNotHydrateContainerInstance?: (
156 + parentContainer: C,
157 + instance: I | TI,
158 + ) => void,
159 + didNotHydrateInstance?: (
160 + parentType: T,
161 + parentProps: P,
162 + parentInstance: I,
163 + instance: I | TI,
164 + ) => void,
165 + didNotFindHydratableContainerInstance?: (
166 + parentContainer: C,
167 + type: T,
168 + props: P,
169 + ) => void,
170 + didNotFindHydratableContainerTextInstance?: (
171 + parentContainer: C,
172 + text: string,
173 + ) => void,
174 didNotFindHydratableInstance?: (
145 - parentInstance: I | C,
175 + parentType: T,
176 + parentProps: P,
177 + parentInstance: I,
178 type: T,
179 props: P,
180 ) => void,
181 didNotFindHydratableTextInstance?: (
150 - parentInstance: I | C,
182 + parentType: T,
183 + parentProps: P,
184 + parentInstance: I,
185 text: string,
186 ) => void,
187
src/renderers/shared/server/ReactPartialRenderer.js
+1
@@ -251,6 +251,7 @@ var RESERVED_PROPS = {
251 children: null,
252 dangerouslySetInnerHTML: null,
253 suppressContentEditableWarning: null,
254 + suppressHydrationWarning: null,
255 };
256
257 function createOpenTagMarkup(