Fix suppressHydrationWarning not working in production (#24271)
dan committed
Apr 4, 2022 at 16:23 UTC
fc47cb1b61ac012f9bd6d7251eb19fcecf364a3b
3 files changed
+101
-11
packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
+92
@@ -2811,4 +2811,96 @@ describe('ReactDOMFizzServer', () => {
2811
</ul>,
2812
);
2813
});
2814
+
2815
+ // @gate experimental
2816
+ it('suppresses and fixes text mismatches with suppressHydrationWarning', async () => {
2817
+ function App({isClient}) {
2818
+ return (
2819
+ <div>
2820
+ <span
2821
+ suppressHydrationWarning={true}
2822
+ data-attr={isClient ? 'client-attr' : 'server-attr'}>
2823
+ {isClient ? 'Client Text' : 'Server Text'}
2824
+ </span>
2825
+ <span suppressHydrationWarning={true}>{isClient ? 2 : 1}</span>
2826
+ <span suppressHydrationWarning={true}>
2827
+ hello,{isClient ? 'client' : 'server'}
2828
+ </span>
2829
+ </div>
2830
+ );
2831
+ }
2832
+ await act(async () => {
2833
+ const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
2834
+ <App isClient={false} />,
2835
+ );
2836
+ pipe(writable);
2837
+ });
2838
+ expect(getVisibleChildren(container)).toEqual(
2839
+ <div>
2840
+ <span data-attr="server-attr">Server Text</span>
2841
+ <span>1</span>
2842
+ <span>
2843
+ {'hello,'}
2844
+ {'server'}
2845
+ </span>
2846
+ </div>,
2847
+ );
2848
+ ReactDOMClient.hydrateRoot(container, <App isClient={true} />, {
2849
+ onRecoverableError(error) {
2850
+ // Don't miss a hydration error. There should be none.
2851
+ Scheduler.unstable_yieldValue(error.message);
2852
+ },
2853
+ });
2854
+ expect(Scheduler).toFlushAndYield([]);
2855
+ // The text mismatch should be *silently* fixed. Even in production.
2856
+ // The attribute mismatch should be ignored and not fixed.
2857
+ expect(getVisibleChildren(container)).toEqual(
2858
+ <div>
2859
+ <span data-attr="server-attr">Client Text</span>
2860
+ <span>2</span>
2861
+ <span>
2862
+ {'hello,'}
2863
+ {'client'}
2864
+ </span>
2865
+ </div>,
2866
+ );
2867
+ });
2868
+
2869
+ // @gate experimental
2870
+ it('suppresses and does not fix html mismatches with suppressHydrationWarning', async () => {
2871
+ function App({isClient}) {
2872
+ return (
2873
+ <div>
2874
+ <p
2875
+ suppressHydrationWarning={true}
2876
+ dangerouslySetInnerHTML={{
2877
+ __html: isClient ? 'Client HTML' : 'Server HTML',
2878
+ }}
2879
+ />
2880
+ </div>
2881
+ );
2882
+ }
2883
+ await act(async () => {
2884
+ const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
2885
+ <App isClient={false} />,
2886
+ );
2887
+ pipe(writable);
2888
+ });
2889
+ expect(getVisibleChildren(container)).toEqual(
2890
+ <div>
2891
+ <p>Server HTML</p>
2892
+ </div>,
2893
+ );
2894
+ ReactDOMClient.hydrateRoot(container, <App isClient={true} />, {
2895
+ onRecoverableError(error) {
2896
+ Scheduler.unstable_yieldValue(error.message);
2897
+ },
2898
+ });
2899
+ expect(Scheduler).toFlushAndYield([]);
2900
+ expect(getVisibleChildren(container)).toEqual(
2901
+ <div>
2902
+ <p>Server HTML</p>
2903
+ </div>,
2904
+ );
2905
+ });
2906
});
packages/react-dom/src/client/ReactDOMComponent.js
+8
-7
@@ -91,7 +91,6 @@ const STYLE = 'style';
91
const HTML = '__html';
92
93
let warnedUnknownTags;
94
-let suppressHydrationWarning;
94
95
let validatePropertiesInDevelopment;
96
let warnForPropDifference;
@@ -875,7 +874,6 @@ export function diffHydratedProperties(
874
let extraAttributeNames: Set<string>;
875
876
if (__DEV__) {
878
- suppressHydrationWarning = rawProps[SUPPRESS_HYDRATION_WARNING] === true;
877
isCustomComponentTag = isCustomComponent(tag, rawProps);
878
validatePropertiesInDevelopment(tag, rawProps);
879
}
@@ -984,7 +982,7 @@ export function diffHydratedProperties(
982
// TODO: Should we use domElement.firstChild.nodeValue to compare?
983
if (typeof nextProp === 'string') {
984
if (domElement.textContent !== nextProp) {
987
- if (!suppressHydrationWarning) {
985
+ if (rawProps[SUPPRESS_HYDRATION_WARNING] !== true) {
986
checkForUnmatchedText(
987
domElement.textContent,
988
nextProp,
@@ -996,7 +994,7 @@ export function diffHydratedProperties(
994
}
995
} else if (typeof nextProp === 'number') {
996
if (domElement.textContent !== '' + nextProp) {
999
- if (!suppressHydrationWarning) {
997
+ if (rawProps[SUPPRESS_HYDRATION_WARNING] !== true) {
998
checkForUnmatchedText(
999
domElement.textContent,
1000
nextProp,
@@ -1028,7 +1026,7 @@ export function diffHydratedProperties(
1026
isCustomComponentTag && enableCustomElementPropertySupport
1027
? null
1028
: getPropertyInfo(propKey);
1031
- if (suppressHydrationWarning) {
1029
+ if (rawProps[SUPPRESS_HYDRATION_WARNING] === true) {
1030
// Don't bother comparing. We're ignoring all these warnings.
1031
} else if (
1032
propKey === SUPPRESS_CONTENT_EDITABLE_WARNING ||
@@ -1150,8 +1148,11 @@ export function diffHydratedProperties(
1148
1149
if (__DEV__) {
1150
if (shouldWarnDev) {
1153
- // $FlowFixMe - Should be inferred as not undefined.
1154
- if (extraAttributeNames.size > 0 && !suppressHydrationWarning) {
1151
+ if (
1152
+ // $FlowFixMe - Should be inferred as not undefined.
1153
+ extraAttributeNames.size > 0 &&
1154
+ rawProps[SUPPRESS_HYDRATION_WARNING] !== true
1155
+ ) {
1156
// $FlowFixMe - Should be inferred as not undefined.
1157
warnForExtraAttributes(extraAttributeNames);
1158
}
packages/react-dom/src/client/ReactDOMHostConfig.js
+1
-4
@@ -132,10 +132,7 @@ type SelectionInformation = {|
132
selectionRange: mixed,
133
|};
134
135
-let SUPPRESS_HYDRATION_WARNING;
136
-if (__DEV__) {
137
- SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';
138
-}
135
+const SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';
136
137
const SUSPENSE_START_DATA = '$';
138
const SUSPENSE_END_DATA = '/$';