Skip to content

Commit fc47cb1

Browse files
authored
Fix suppressHydrationWarning not working in production (facebook#24271)
1 parent 985272e commit fc47cb1

File tree

3 files changed

+101
-11
lines changed

3 files changed

+101
-11
lines changed

packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js

+92
Original file line numberDiff line numberDiff line change
@@ -2811,4 +2811,96 @@ describe('ReactDOMFizzServer', () => {
28112811
</ul>,
28122812
);
28132813
});
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+
});
28142906
});

packages/react-dom/src/client/ReactDOMComponent.js

+8-7
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ const STYLE = 'style';
9191
const HTML = '__html';
9292

9393
let warnedUnknownTags;
94-
let suppressHydrationWarning;
9594

9695
let validatePropertiesInDevelopment;
9796
let warnForPropDifference;
@@ -875,7 +874,6 @@ export function diffHydratedProperties(
875874
let extraAttributeNames: Set<string>;
876875

877876
if (__DEV__) {
878-
suppressHydrationWarning = rawProps[SUPPRESS_HYDRATION_WARNING] === true;
879877
isCustomComponentTag = isCustomComponent(tag, rawProps);
880878
validatePropertiesInDevelopment(tag, rawProps);
881879
}
@@ -984,7 +982,7 @@ export function diffHydratedProperties(
984982
// TODO: Should we use domElement.firstChild.nodeValue to compare?
985983
if (typeof nextProp === 'string') {
986984
if (domElement.textContent !== nextProp) {
987-
if (!suppressHydrationWarning) {
985+
if (rawProps[SUPPRESS_HYDRATION_WARNING] !== true) {
988986
checkForUnmatchedText(
989987
domElement.textContent,
990988
nextProp,
@@ -996,7 +994,7 @@ export function diffHydratedProperties(
996994
}
997995
} else if (typeof nextProp === 'number') {
998996
if (domElement.textContent !== '' + nextProp) {
999-
if (!suppressHydrationWarning) {
997+
if (rawProps[SUPPRESS_HYDRATION_WARNING] !== true) {
1000998
checkForUnmatchedText(
1001999
domElement.textContent,
10021000
nextProp,
@@ -1028,7 +1026,7 @@ export function diffHydratedProperties(
10281026
isCustomComponentTag && enableCustomElementPropertySupport
10291027
? null
10301028
: getPropertyInfo(propKey);
1031-
if (suppressHydrationWarning) {
1029+
if (rawProps[SUPPRESS_HYDRATION_WARNING] === true) {
10321030
// Don't bother comparing. We're ignoring all these warnings.
10331031
} else if (
10341032
propKey === SUPPRESS_CONTENT_EDITABLE_WARNING ||
@@ -1150,8 +1148,11 @@ export function diffHydratedProperties(
11501148

11511149
if (__DEV__) {
11521150
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+
) {
11551156
// $FlowFixMe - Should be inferred as not undefined.
11561157
warnForExtraAttributes(extraAttributeNames);
11571158
}

packages/react-dom/src/client/ReactDOMHostConfig.js

+1-4
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,7 @@ type SelectionInformation = {|
132132
selectionRange: mixed,
133133
|};
134134

135-
let SUPPRESS_HYDRATION_WARNING;
136-
if (__DEV__) {
137-
SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';
138-
}
135+
const SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';
139136

140137
const SUSPENSE_START_DATA = '$';
141138
const SUSPENSE_END_DATA = '/$';

0 commit comments

Comments
 (0)