Skip to content

Commit c55ffb3

Browse files
authored
Fix false positive hydration warning for SVG attributes (#10676)
* Fix false positive hydration warning for SVG attributes * Undo the generic fix * Pass namespace through and use it to determine sensitivity
1 parent 65b9ad9 commit c55ffb3

File tree

8 files changed

+63
-18
lines changed

8 files changed

+63
-18
lines changed

src/renderers/dom/fiber/ReactDOMFiberComponent.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,7 @@ var ReactDOMFiberComponent = {
776776
domElement: Element,
777777
tag: string,
778778
rawProps: Object,
779+
parentNamespace: string,
779780
rootContainerElement: Element | Document,
780781
): null | Array<mixed> {
781782
if (__DEV__) {
@@ -912,6 +913,8 @@ var ReactDOMFiberComponent = {
912913
case 'selected':
913914
break;
914915
default:
916+
// Intentionally use the original name.
917+
// See discussion in https://github.com/facebook/react/pull/10676.
915918
extraAttributeNames.add(attributes[i].name);
916919
}
917920
}
@@ -1007,8 +1010,17 @@ var ReactDOMFiberComponent = {
10071010
nextProp,
10081011
);
10091012
} else {
1010-
// $FlowFixMe - Should be inferred as not undefined.
1011-
extraAttributeNames.delete(propKey.toLowerCase());
1013+
let ownNamespace = parentNamespace;
1014+
if (ownNamespace === HTML_NAMESPACE) {
1015+
ownNamespace = getIntrinsicNamespace(tag);
1016+
}
1017+
if (ownNamespace === HTML_NAMESPACE) {
1018+
// $FlowFixMe - Should be inferred as not undefined.
1019+
extraAttributeNames.delete(propKey.toLowerCase());
1020+
} else {
1021+
// $FlowFixMe - Should be inferred as not undefined.
1022+
extraAttributeNames.delete(propKey);
1023+
}
10121024
serverValue = DOMPropertyOperations.getValueForAttribute(
10131025
domElement,
10141026
propKey,

src/renderers/dom/fiber/ReactDOMFiberEntry.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,13 +488,27 @@ var DOMRenderer = ReactFiberReconciler({
488488
type: string,
489489
props: Props,
490490
rootContainerInstance: Container,
491+
hostContext: HostContext,
491492
internalInstanceHandle: Object,
492493
): null | Array<mixed> {
493494
precacheFiberNode(internalInstanceHandle, instance);
494495
// TODO: Possibly defer this until the commit phase where all the events
495496
// get attached.
496497
updateFiberProps(instance, props);
497-
return diffHydratedProperties(instance, type, props, rootContainerInstance);
498+
let parentNamespace: string;
499+
if (__DEV__) {
500+
const hostContextDev = ((hostContext: any): HostContextDev);
501+
parentNamespace = hostContextDev.namespace;
502+
} else {
503+
parentNamespace = ((hostContext: any): HostContextProd);
504+
}
505+
return diffHydratedProperties(
506+
instance,
507+
type,
508+
props,
509+
parentNamespace,
510+
rootContainerInstance,
511+
);
498512
},
499513

500514
hydrateTextInstance(

src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,19 +1325,30 @@ describe('ReactDOMServerIntegration', () => {
13251325
expect(e.namespaceURI).toBe('http://www.w3.org/2000/svg');
13261326
});
13271327

1328-
itRenders('svg child element', async render => {
1329-
let e = await render(
1330-
<svg><image xlinkHref="http://i.imgur.com/w7GCRPb.png" /></svg>,
1331-
);
1332-
e = e.firstChild;
1328+
itRenders('svg child element with an attribute', async render => {
1329+
let e = await render(<svg viewBox="0 0 0 0" />);
13331330
expect(e.childNodes.length).toBe(0);
1334-
expect(e.tagName).toBe('image');
1331+
expect(e.tagName).toBe('svg');
13351332
expect(e.namespaceURI).toBe('http://www.w3.org/2000/svg');
1336-
expect(e.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBe(
1337-
'http://i.imgur.com/w7GCRPb.png',
1338-
);
1333+
expect(e.getAttribute('viewBox')).toBe('0 0 0 0');
13391334
});
13401335

1336+
itRenders(
1337+
'svg child element with a namespace attribute',
1338+
async render => {
1339+
let e = await render(
1340+
<svg><image xlinkHref="http://i.imgur.com/w7GCRPb.png" /></svg>,
1341+
);
1342+
e = e.firstChild;
1343+
expect(e.childNodes.length).toBe(0);
1344+
expect(e.tagName).toBe('image');
1345+
expect(e.namespaceURI).toBe('http://www.w3.org/2000/svg');
1346+
expect(e.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBe(
1347+
'http://i.imgur.com/w7GCRPb.png',
1348+
);
1349+
},
1350+
);
1351+
13411352
itRenders('svg child element with a badly cased alias', async render => {
13421353
let e = await render(
13431354
<svg><image xlinkhref="http://i.imgur.com/w7GCRPb.png" /></svg>,

src/renderers/shared/fiber/ReactFiberBeginWork.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ if (__DEV__) {
7272
module.exports = function<T, P, I, TI, PI, C, CX, PL>(
7373
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
7474
hostContext: HostContext<C, CX>,
75-
hydrationContext: HydrationContext<C>,
75+
hydrationContext: HydrationContext<C, CX>,
7676
scheduleUpdate: (fiber: Fiber, priorityLevel: PriorityLevel) => void,
7777
getPriorityContext: (fiber: Fiber, forceAsync: boolean) => PriorityLevel,
7878
) {

src/renderers/shared/fiber/ReactFiberCompleteWork.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ var invariant = require('fbjs/lib/invariant');
5353
module.exports = function<T, P, I, TI, PI, C, CX, PL>(
5454
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
5555
hostContext: HostContext<C, CX>,
56-
hydrationContext: HydrationContext<C>,
56+
hydrationContext: HydrationContext<C, CX>,
5757
) {
5858
const {
5959
createInstance,
@@ -289,6 +289,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
289289
prepareToHydrateHostInstance(
290290
workInProgress,
291291
rootContainerInstance,
292+
currentHostContext,
292293
)
293294
) {
294295
// If changes to the hydrated node needs to be applied at the

src/renderers/shared/fiber/ReactFiberHydrationContext.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,22 @@ const {Deletion, Placement} = require('ReactTypeOfSideEffect');
2222

2323
const {createFiberFromHostInstanceForDeletion} = require('ReactFiber');
2424

25-
export type HydrationContext<C> = {
25+
export type HydrationContext<C, CX> = {
2626
enterHydrationState(fiber: Fiber): boolean,
2727
resetHydrationState(): void,
2828
tryToClaimNextHydratableInstance(fiber: Fiber): void,
29-
prepareToHydrateHostInstance(fiber: Fiber, rootContainerInstance: C): boolean,
29+
prepareToHydrateHostInstance(
30+
fiber: Fiber,
31+
rootContainerInstance: C,
32+
hostContext: CX,
33+
): boolean,
3034
prepareToHydrateHostTextInstance(fiber: Fiber): boolean,
3135
popHydrationState(fiber: Fiber): boolean,
3236
};
3337

3438
module.exports = function<T, P, I, TI, PI, C, CX, PL>(
3539
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
36-
): HydrationContext<C> {
40+
): HydrationContext<C, CX> {
3741
const {
3842
shouldSetTextContent,
3943
canHydrateInstance,
@@ -218,13 +222,15 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
218222
function prepareToHydrateHostInstance(
219223
fiber: Fiber,
220224
rootContainerInstance: C,
225+
hostContext: CX,
221226
): boolean {
222227
const instance: I = fiber.stateNode;
223228
const updatePayload = hydrateInstance(
224229
instance,
225230
fiber.type,
226231
fiber.memoizedProps,
227232
rootContainerInstance,
233+
hostContext,
228234
fiber,
229235
);
230236
// TODO: Type this specific to this type of component.

src/renderers/shared/fiber/ReactFiberReconciler.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ export type HostConfig<T, P, I, TI, PI, C, CX, PL> = {
133133
type: T,
134134
props: P,
135135
rootContainerInstance: C,
136+
hostContext: CX,
136137
internalInstanceHandle: OpaqueHandle,
137138
) => null | PL,
138139
hydrateTextInstance?: (

src/renderers/shared/fiber/ReactFiberScheduler.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
152152
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
153153
) {
154154
const hostContext = ReactFiberHostContext(config);
155-
const hydrationContext: HydrationContext<C> = ReactFiberHydrationContext(
155+
const hydrationContext: HydrationContext<C, CX> = ReactFiberHydrationContext(
156156
config,
157157
);
158158
const {popHostContainer, popHostContext, resetHostContainer} = hostContext;

0 commit comments

Comments
 (0)