Skip to content

Commit

Permalink
Pass mismatching instance
Browse files Browse the repository at this point in the history
  • Loading branch information
gaearon committed Apr 20, 2022
1 parent 21a7b3e commit 2fe2a92
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 25 deletions.
14 changes: 10 additions & 4 deletions packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,11 +243,17 @@ describe('ReactDOMServerHydration', () => {
describe('extra nodes on the client', () => {
describe('extra elements on the client', () => {
// @gate __DEV__
fit('warns when client renders an extra element as only child', () => {
it('warns when client renders an extra element as only child', () => {
function Mismatch({isClient}) {
return (
<div className="parent" style={{ opacity: 1}} onClick={() => {}}>
{isClient && <main className="only" style={{ opacity: 1 }} onClick={() => {}} />}
<div className="parent" style={{opacity: 1}} onClick={() => {}}>
{isClient && (
<main
className="only"
style={{opacity: 1}}
onClick={() => {}}
/>
)}
</div>
);
}
Expand Down Expand Up @@ -499,7 +505,7 @@ describe('ReactDOMServerHydration', () => {
});

// @gate __DEV__
it('warns when server renders an extra element in the beginning', () => {
fit('warns when server renders an extra element in the beginning', () => {
function Mismatch({isClient}) {
return (
<div className="parent">
Expand Down
26 changes: 15 additions & 11 deletions packages/react-dom/src/client/ReactDOMComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -1348,13 +1348,17 @@ function formatDiffForExtraServerNode(parentNode, child) {
return formatElement(parentNode, ' ', formattedChildren);
}

function formatDiffForExtraClientNode(parentNode, tag, props) {
// TODO
function formatDiffForExtraClientNode(parentNode, tag, props, mismatchNode) {
let formattedSibling = null;
// const prevSibling = findPreviousSiblingForDiff(child);
// if (prevSibling !== null) {
// formattedSibling = formatNode(prevSibling, ' ');
// }
let prevSibling = null;
if (mismatchNode !== null) {
prevSibling = findPreviousSiblingForDiff(mismatchNode);
} else if (parentNode.lastChild !== null) {
prevSibling = parentNode.lastChild;
}
if (prevSibling !== null) {
formattedSibling = formatNode(prevSibling, ' ');
}
let formattedChildren = '';
if (formattedSibling !== null) {
if (findPreviousSiblingForDiff(prevSibling) !== null) {
Expand All @@ -1364,13 +1368,12 @@ function formatDiffForExtraClientNode(parentNode, tag, props) {
}
formattedChildren += formatReactElement(tag, props, '+ ');
formattedChildren += ' <-- client';
// if (findNextSiblingForDiff(child) !== null) {
// formattedChildren += '\n ...';
// }
if (mismatchNode !== null) {
formattedChildren += '\n ...';
}
return formatElement(parentNode, ' ', formattedChildren);
}


export function warnForDeletedHydratableElement(
parentNode: Element | Document | DocumentFragment,
child: Element,
Expand Down Expand Up @@ -1411,6 +1414,7 @@ export function warnForInsertedHydratedElement(
parentNode: Element | Document | DocumentFragment,
tag: string,
props: Object,
mismatchInstance,
) {
if (__DEV__) {
if (didWarnInvalidHydration) {
Expand All @@ -1421,7 +1425,7 @@ export function warnForInsertedHydratedElement(
'The content rendered by the server and the client did not match ' +
'because the client has rendered an extra element. ' +
'The mismatch occurred inside of this parent:\n\n%s',
formatDiffForExtraClientNode(parentNode, tag, props),
formatDiffForExtraClientNode(parentNode, tag, props, mismatchInstance),
);
}
}
Expand Down
24 changes: 21 additions & 3 deletions packages/react-dom/src/client/ReactDOMHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -1020,9 +1020,15 @@ export function didNotFindHydratableInstanceWithinContainer(
parentContainer: Container,
type: string,
props: Props,
lastHydratedChild,
) {
if (__DEV__) {
warnForInsertedHydratedElement(parentContainer, type, props);
warnForInsertedHydratedElement(
parentContainer,
type,
props,
lastHydratedChild,
);
}
}

Expand All @@ -1047,12 +1053,18 @@ export function didNotFindHydratableInstanceWithinSuspenseInstance(
parentInstance: SuspenseInstance,
type: string,
props: Props,
lastHydratedChild,
) {
if (__DEV__) {
// $FlowFixMe: Only Element or Document can be parent nodes.
const parentNode: Element | Document | null = parentInstance.parentNode;
if (parentNode !== null)
warnForInsertedHydratedElement(parentNode, type, props);
warnForInsertedHydratedElement(
parentNode,
type,
props,
lastHydratedChild,
);
}
}

Expand Down Expand Up @@ -1083,10 +1095,16 @@ export function didNotFindHydratableInstance(
type: string,
props: Props,
isConcurrentMode: boolean,
lastHydratedChild,
) {
if (__DEV__) {
if (isConcurrentMode || parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
warnForInsertedHydratedElement(parentInstance, type, props);
warnForInsertedHydratedElement(
parentInstance,
type,
props,
lastHydratedChild,
);
}
}
}
Expand Down
29 changes: 22 additions & 7 deletions packages/react-reconciler/src/ReactFiberHydrationContext.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,11 @@ function deleteHydratableInstance(
}
}

function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) {
function warnNonhydratedInstance(
returnFiber: Fiber,
fiber: Fiber,
mismatchInstance,
) {
if (__DEV__) {
if (didSuspend) {
// Inside a boundary that already suspended. We're currently rendering the
Expand All @@ -214,6 +218,7 @@ function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) {
parentContainer,
type,
props,
mismatchInstance,
);
break;
case HostText:
Expand Down Expand Up @@ -249,6 +254,7 @@ function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) {
props,
// TODO: Delete this argument when we remove the legacy root API.
isConcurrentMode,
mismatchInstance,
);
break;
}
Expand Down Expand Up @@ -289,6 +295,7 @@ function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) {
parentInstance,
type,
props,
mismatchInstance,
);
break;
case HostText:
Expand All @@ -311,9 +318,13 @@ function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) {
}
}
}
function insertNonHydratedInstance(returnFiber: Fiber, fiber: Fiber) {
function insertNonHydratedInstance(
returnFiber: Fiber,
fiber: Fiber,
mismatchInstance,
) {
fiber.flags = (fiber.flags & ~Hydrating) | Placement;
warnNonhydratedInstance(returnFiber, fiber);
warnNonhydratedInstance(returnFiber, fiber, lastHydratedChild);
}

function tryHydrate(fiber, nextInstance) {
Expand Down Expand Up @@ -396,19 +407,19 @@ function tryToClaimNextHydratableInstance(fiber: Fiber): void {
let nextInstance = nextHydratableInstance;
if (!nextInstance) {
if (shouldClientRenderOnMismatch(fiber)) {
warnNonhydratedInstance((hydrationParentFiber: any), fiber);
warnNonhydratedInstance((hydrationParentFiber: any), fiber, nextInstance);
throwOnHydrationMismatch(fiber);
}
// Nothing to hydrate. Make it an insertion.
insertNonHydratedInstance((hydrationParentFiber: any), fiber);
insertNonHydratedInstance((hydrationParentFiber: any), fiber, nextInstance);
isHydrating = false;
hydrationParentFiber = fiber;
return;
}
const firstAttemptedInstance = nextInstance;
if (!tryHydrate(fiber, nextInstance)) {
if (shouldClientRenderOnMismatch(fiber)) {
warnNonhydratedInstance((hydrationParentFiber: any), fiber);
warnNonhydratedInstance((hydrationParentFiber: any), fiber, nextInstance);
throwOnHydrationMismatch(fiber);
}
// If we can't hydrate this instance let's try the next one.
Expand All @@ -418,7 +429,11 @@ function tryToClaimNextHydratableInstance(fiber: Fiber): void {
const prevHydrationParentFiber: Fiber = (hydrationParentFiber: any);
if (!nextInstance || !tryHydrate(fiber, nextInstance)) {
// Nothing to hydrate. Make it an insertion.
insertNonHydratedInstance((hydrationParentFiber: any), fiber);
insertNonHydratedInstance(
(hydrationParentFiber: any),
fiber,
nextInstance,
);
isHydrating = false;
hydrationParentFiber = fiber;
return;
Expand Down

0 comments on commit 2fe2a92

Please sign in to comment.