Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Print server nodes for hydration mismatch #24159

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 60 additions & 6 deletions packages/react-dom/src/client/ReactDOMComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ import {
let didWarnInvalidHydration = false;
let didWarnScriptTags = false;

let isJsdomDev = false;
if (__DEV__) {
if (
typeof navigator !== 'undefined' &&
navigator != null &&
typeof navigator.userAgent === 'string' &&
navigator.userAgent.includes('jsdom')
) {
isJsdomDev = true;
}
}

const DANGEROUSLY_SET_INNER_HTML = 'dangerouslySetInnerHTML';
const SUPPRESS_CONTENT_EDITABLE_WARNING = 'suppressContentEditableWarning';
const SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';
Expand Down Expand Up @@ -136,6 +148,7 @@ if (__DEV__) {
propName: string,
serverValue: mixed,
clientValue: mixed,
domElement: Element,
) {
if (didWarnInvalidHydration) {
return;
Expand All @@ -156,9 +169,15 @@ if (__DEV__) {
JSON.stringify(normalizedServerValue),
JSON.stringify(normalizedClientValue),
);
if (!isJsdomDev) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They're pretty noisy in Node environment because it starts printing fibers due to the reference inside. Plus not very helpful anyway. Not sure if there's a better way to detect.

console['error']('Server HTML:', domElement);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Untransformed because we don't want Warning: prefix or component stack.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess DevTools adds component stack anyway.

}
};

warnForExtraAttributes = function(attributeNames: Set<string>) {
warnForExtraAttributes = function(
attributeNames: Set<string>,
domElement: Element,
) {
if (didWarnInvalidHydration) {
return;
}
Expand All @@ -168,6 +187,9 @@ if (__DEV__) {
names.push(name);
});
console.error('Extra attributes from the server: %s', names);
if (!isJsdomDev) {
console['error']('Server HTML:', domElement);
}
};

warnForInvalidEventListener = function(registrationName, listener) {
Expand Down Expand Up @@ -1046,7 +1068,12 @@ export function diffHydratedProperties(
if (nextHtml != null) {
const expectedHTML = normalizeHTML(domElement, nextHtml);
if (expectedHTML !== serverHTML) {
warnForPropDifference(propKey, serverHTML, expectedHTML);
warnForPropDifference(
propKey,
serverHTML,
expectedHTML,
domElement,
);
}
}
} else if (propKey === STYLE) {
Expand All @@ -1057,7 +1084,12 @@ export function diffHydratedProperties(
const expectedStyle = createDangerousStringForStyles(nextProp);
serverValue = domElement.getAttribute('style');
if (expectedStyle !== serverValue) {
warnForPropDifference(propKey, serverValue, expectedStyle);
warnForPropDifference(
propKey,
serverValue,
expectedStyle,
domElement,
);
}
}
} else if (
Expand Down Expand Up @@ -1086,7 +1118,7 @@ export function diffHydratedProperties(
serverValue = getValueForAttribute(domElement, propKey, nextProp);

if (nextProp !== serverValue) {
warnForPropDifference(propKey, serverValue, nextProp);
warnForPropDifference(propKey, serverValue, nextProp, domElement);
}
} else if (
!shouldIgnoreAttribute(propKey, propertyInfo, isCustomComponentTag) &&
Expand Down Expand Up @@ -1142,7 +1174,7 @@ export function diffHydratedProperties(
nextProp !== serverValue &&
!isMismatchDueToBadCasing
) {
warnForPropDifference(propKey, serverValue, nextProp);
warnForPropDifference(propKey, serverValue, nextProp, domElement);
}
}
}
Expand All @@ -1153,7 +1185,7 @@ export function diffHydratedProperties(
// $FlowFixMe - Should be inferred as not undefined.
if (extraAttributeNames.size > 0 && !suppressHydrationWarning) {
// $FlowFixMe - Should be inferred as not undefined.
warnForExtraAttributes(extraAttributeNames);
warnForExtraAttributes(extraAttributeNames, domElement);
}
}
}
Expand Down Expand Up @@ -1213,6 +1245,14 @@ export function warnForDeletedHydratableElement(
child.nodeName.toLowerCase(),
parentNode.nodeName.toLowerCase(),
);
if (!isJsdomDev) {
console['error'](
'Server HTML:',
parentNode,
'\n\nFirst mismatching child:',
child,
);
}
}
}

Expand All @@ -1230,6 +1270,14 @@ export function warnForDeletedHydratableText(
child.nodeValue,
parentNode.nodeName.toLowerCase(),
);
if (!isJsdomDev) {
console['error'](
'Server HTML:',
parentNode,
'\n\nFirst mismatching child:',
child,
);
}
}
}

Expand All @@ -1248,6 +1296,9 @@ export function warnForInsertedHydratedElement(
tag,
parentNode.nodeName.toLowerCase(),
);
if (!isJsdomDev) {
console['error']('Server HTML:', parentNode);
}
}
}

Expand All @@ -1272,6 +1323,9 @@ export function warnForInsertedHydratedText(
text,
parentNode.nodeName.toLowerCase(),
);
if (!isJsdomDev) {
console['error']('Server HTML:', parentNode);
}
}
}

Expand Down