Skip to content

Commit 2ec2aae

Browse files
authored
Add Diffs to Hydration Warnings (#28512)
Stacked on #28502. This builds on the mechanism in #28502 by adding a diff of everything we've collected so far to the thrown error or logged error. This isn't actually a longest common subsequence diff. This means that there are certain cases that can appear confusing such as a node being added/removed when it really would've appeared later in the list. In fact once a node mismatches, we abort rendering so we don't have the context of what would've been rendered. It's not quite right to use the result of the recovery render because it can use client-only code paths using useSyncExternalStore which would yield false differences. That's why diffing the HTML isn't quite right. I also present abstract components in the stack, these are presented with the client props and no diff since we don't have the props that were on the server. The lack of difference might be confusing but it's useful for context. The main thing that's data new here is that we're adding some siblings and props for context. Examples in the [snapshot commit](e14532f).
1 parent f7aa5e0 commit 2ec2aae

File tree

4 files changed

+926
-33
lines changed

4 files changed

+926
-33
lines changed

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ function warnForExtraAttributes(
251251
) {
252252
if (__DEV__) {
253253
attributeNames.forEach(function (attributeName) {
254-
serverDifferences[attributeName] =
254+
serverDifferences[getPropNameFromAttributeName(attributeName)] =
255255
attributeName === 'style'
256256
? getStylesObjectFromElement(domElement)
257257
: domElement.getAttribute(attributeName);
@@ -1829,12 +1829,24 @@ function getPossibleStandardName(propName: string): string | null {
18291829
return null;
18301830
}
18311831

1832+
function getPropNameFromAttributeName(attrName: string): string {
1833+
switch (attrName) {
1834+
case 'class':
1835+
return 'className';
1836+
case 'for':
1837+
return 'htmlFor';
1838+
// TODO: The rest of the aliases.
1839+
default:
1840+
return attrName;
1841+
}
1842+
}
1843+
18321844
export function getPropsFromElement(domElement: Element): Object {
18331845
const serverDifferences: {[propName: string]: mixed} = {};
18341846
const attributes = domElement.attributes;
18351847
for (let i = 0; i < attributes.length; i++) {
18361848
const attr = attributes[i];
1837-
serverDifferences[attr.name] =
1849+
serverDifferences[getPropNameFromAttributeName(attr.name)] =
18381850
attr.name.toLowerCase() === 'style'
18391851
? getStylesObjectFromElement(domElement)
18401852
: attr.value;

0 commit comments

Comments
 (0)