Skip to content

Commit

Permalink
Fix suppressHydrationWarning not working in production (facebook#24271)
Browse files Browse the repository at this point in the history
  • Loading branch information
gaearon authored and zhengjitf committed Apr 15, 2022
1 parent 080e1c4 commit 8a70078
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 11 deletions.
92 changes: 92 additions & 0 deletions packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2811,4 +2811,96 @@ describe('ReactDOMFizzServer', () => {
</ul>,
);
});

// @gate experimental
it('suppresses and fixes text mismatches with suppressHydrationWarning', async () => {
function App({isClient}) {
return (
<div>
<span
suppressHydrationWarning={true}
data-attr={isClient ? 'client-attr' : 'server-attr'}>
{isClient ? 'Client Text' : 'Server Text'}
</span>
<span suppressHydrationWarning={true}>{isClient ? 2 : 1}</span>
<span suppressHydrationWarning={true}>
hello,{isClient ? 'client' : 'server'}
</span>
</div>
);
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
<App isClient={false} />,
);
pipe(writable);
});
expect(getVisibleChildren(container)).toEqual(
<div>
<span data-attr="server-attr">Server Text</span>
<span>1</span>
<span>
{'hello,'}
{'server'}
</span>
</div>,
);
ReactDOMClient.hydrateRoot(container, <App isClient={true} />, {
onRecoverableError(error) {
// Don't miss a hydration error. There should be none.
Scheduler.unstable_yieldValue(error.message);
},
});
expect(Scheduler).toFlushAndYield([]);
// The text mismatch should be *silently* fixed. Even in production.
// The attribute mismatch should be ignored and not fixed.
expect(getVisibleChildren(container)).toEqual(
<div>
<span data-attr="server-attr">Client Text</span>
<span>2</span>
<span>
{'hello,'}
{'client'}
</span>
</div>,
);
});

// @gate experimental
it('suppresses and does not fix html mismatches with suppressHydrationWarning', async () => {
function App({isClient}) {
return (
<div>
<p
suppressHydrationWarning={true}
dangerouslySetInnerHTML={{
__html: isClient ? 'Client HTML' : 'Server HTML',
}}
/>
</div>
);
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
<App isClient={false} />,
);
pipe(writable);
});
expect(getVisibleChildren(container)).toEqual(
<div>
<p>Server HTML</p>
</div>,
);
ReactDOMClient.hydrateRoot(container, <App isClient={true} />, {
onRecoverableError(error) {
Scheduler.unstable_yieldValue(error.message);
},
});
expect(Scheduler).toFlushAndYield([]);
expect(getVisibleChildren(container)).toEqual(
<div>
<p>Server HTML</p>
</div>,
);
});
});
15 changes: 8 additions & 7 deletions packages/react-dom/src/client/ReactDOMComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ const STYLE = 'style';
const HTML = '__html';

let warnedUnknownTags;
let suppressHydrationWarning;

let validatePropertiesInDevelopment;
let warnForPropDifference;
Expand Down Expand Up @@ -875,7 +874,6 @@ export function diffHydratedProperties(
let extraAttributeNames: Set<string>;

if (__DEV__) {
suppressHydrationWarning = rawProps[SUPPRESS_HYDRATION_WARNING] === true;
isCustomComponentTag = isCustomComponent(tag, rawProps);
validatePropertiesInDevelopment(tag, rawProps);
}
Expand Down Expand Up @@ -984,7 +982,7 @@ export function diffHydratedProperties(
// TODO: Should we use domElement.firstChild.nodeValue to compare?
if (typeof nextProp === 'string') {
if (domElement.textContent !== nextProp) {
if (!suppressHydrationWarning) {
if (rawProps[SUPPRESS_HYDRATION_WARNING] !== true) {
checkForUnmatchedText(
domElement.textContent,
nextProp,
Expand All @@ -996,7 +994,7 @@ export function diffHydratedProperties(
}
} else if (typeof nextProp === 'number') {
if (domElement.textContent !== '' + nextProp) {
if (!suppressHydrationWarning) {
if (rawProps[SUPPRESS_HYDRATION_WARNING] !== true) {
checkForUnmatchedText(
domElement.textContent,
nextProp,
Expand Down Expand Up @@ -1028,7 +1026,7 @@ export function diffHydratedProperties(
isCustomComponentTag && enableCustomElementPropertySupport
? null
: getPropertyInfo(propKey);
if (suppressHydrationWarning) {
if (rawProps[SUPPRESS_HYDRATION_WARNING] === true) {
// Don't bother comparing. We're ignoring all these warnings.
} else if (
propKey === SUPPRESS_CONTENT_EDITABLE_WARNING ||
Expand Down Expand Up @@ -1150,8 +1148,11 @@ export function diffHydratedProperties(

if (__DEV__) {
if (shouldWarnDev) {
// $FlowFixMe - Should be inferred as not undefined.
if (extraAttributeNames.size > 0 && !suppressHydrationWarning) {
if (
// $FlowFixMe - Should be inferred as not undefined.
extraAttributeNames.size > 0 &&
rawProps[SUPPRESS_HYDRATION_WARNING] !== true
) {
// $FlowFixMe - Should be inferred as not undefined.
warnForExtraAttributes(extraAttributeNames);
}
Expand Down
5 changes: 1 addition & 4 deletions packages/react-dom/src/client/ReactDOMHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,7 @@ type SelectionInformation = {|
selectionRange: mixed,
|};

let SUPPRESS_HYDRATION_WARNING;
if (__DEV__) {
SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';
}
const SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';

const SUSPENSE_START_DATA = '$';
const SUSPENSE_END_DATA = '/$';
Expand Down

0 comments on commit 8a70078

Please sign in to comment.