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
Dan Abramov committed
Sep 13, 2017 at 16:31 UTC
c55ffb37f1b8972dc954b8949e7fca6e8bce6c91
8 files changed
+63
-18
src/renderers/dom/fiber/ReactDOMFiberComponent.js
+14
-2
@@ -776,6 +776,7 @@ var ReactDOMFiberComponent = {
776
domElement: Element,
777
tag: string,
778
rawProps: Object,
779
+ parentNamespace: string,
780
rootContainerElement: Element | Document,
781
): null | Array<mixed> {
782
if (__DEV__) {
@@ -912,6 +913,8 @@ var ReactDOMFiberComponent = {
913
case 'selected':
914
break;
915
default:
916
+ // Intentionally use the original name.
917
+ // See discussion in https://github.com/facebook/react/pull/10676.
918
extraAttributeNames.add(attributes[i].name);
919
}
920
}
@@ -1007,8 +1010,17 @@ var ReactDOMFiberComponent = {
1010
nextProp,
1011
);
1012
} 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
+ }
1024
serverValue = DOMPropertyOperations.getValueForAttribute(
1025
domElement,
1026
propKey,
src/renderers/dom/fiber/ReactDOMFiberEntry.js
+15
-1
@@ -488,13 +488,27 @@ var DOMRenderer = ReactFiberReconciler({
488
type: string,
489
props: Props,
490
rootContainerInstance: Container,
491
+ hostContext: HostContext,
492
internalInstanceHandle: Object,
493
): null | Array<mixed> {
494
precacheFiberNode(internalInstanceHandle, instance);
495
// TODO: Possibly defer this until the commit phase where all the events
496
// get attached.
497
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
+ );
512
},
513
514
hydrateTextInstance(
src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+20
-9
@@ -1325,19 +1325,30 @@ describe('ReactDOMServerIntegration', () => {
1325
expect(e.namespaceURI).toBe('http://www.w3.org/2000/svg');
1326
});
1327
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" />);
1330
expect(e.childNodes.length).toBe(0);
1334
- expect(e.tagName).toBe('image');
1331
+ expect(e.tagName).toBe('svg');
1332
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');
1334
});
1335
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
+
1352
itRenders('svg child element with a badly cased alias', async render => {
1353
let e = await render(
1354
<svg><image xlinkhref="http://i.imgur.com/w7GCRPb.png" /></svg>,
src/renderers/shared/fiber/ReactFiberBeginWork.js
+1
-1
@@ -72,7 +72,7 @@ if (__DEV__) {
72
module.exports = function<T, P, I, TI, PI, C, CX, PL>(
73
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
74
hostContext: HostContext<C, CX>,
75
- hydrationContext: HydrationContext<C>,
75
+ hydrationContext: HydrationContext<C, CX>,
76
scheduleUpdate: (fiber: Fiber, priorityLevel: PriorityLevel) => void,
77
getPriorityContext: (fiber: Fiber, forceAsync: boolean) => PriorityLevel,
78
) {
src/renderers/shared/fiber/ReactFiberCompleteWork.js
+2
-1
@@ -53,7 +53,7 @@ var invariant = require('fbjs/lib/invariant');
53
module.exports = function<T, P, I, TI, PI, C, CX, PL>(
54
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
55
hostContext: HostContext<C, CX>,
56
- hydrationContext: HydrationContext<C>,
56
+ hydrationContext: HydrationContext<C, CX>,
57
) {
58
const {
59
createInstance,
@@ -289,6 +289,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
289
prepareToHydrateHostInstance(
290
workInProgress,
291
rootContainerInstance,
292
+ currentHostContext,
293
)
294
) {
295
// If changes to the hydrated node needs to be applied at the
src/renderers/shared/fiber/ReactFiberHydrationContext.js
+9
-3
@@ -22,18 +22,22 @@ const {Deletion, Placement} = require('ReactTypeOfSideEffect');
22
23
const {createFiberFromHostInstanceForDeletion} = require('ReactFiber');
24
25
-export type HydrationContext<C> = {
25
+export type HydrationContext<C, CX> = {
26
enterHydrationState(fiber: Fiber): boolean,
27
resetHydrationState(): void,
28
tryToClaimNextHydratableInstance(fiber: Fiber): void,
29
- prepareToHydrateHostInstance(fiber: Fiber, rootContainerInstance: C): boolean,
29
+ prepareToHydrateHostInstance(
30
+ fiber: Fiber,
31
+ rootContainerInstance: C,
32
+ hostContext: CX,
33
+ ): boolean,
34
prepareToHydrateHostTextInstance(fiber: Fiber): boolean,
35
popHydrationState(fiber: Fiber): boolean,
36
};
37
38
module.exports = function<T, P, I, TI, PI, C, CX, PL>(
39
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
36
-): HydrationContext<C> {
40
+): HydrationContext<C, CX> {
41
const {
42
shouldSetTextContent,
43
canHydrateInstance,
@@ -218,6 +222,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
222
function prepareToHydrateHostInstance(
223
fiber: Fiber,
224
rootContainerInstance: C,
225
+ hostContext: CX,
226
): boolean {
227
const instance: I = fiber.stateNode;
228
const updatePayload = hydrateInstance(
@@ -225,6 +230,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
230
fiber.type,
231
fiber.memoizedProps,
232
rootContainerInstance,
233
+ hostContext,
234
fiber,
235
);
236
// TODO: Type this specific to this type of component.
src/renderers/shared/fiber/ReactFiberReconciler.js
+1
@@ -133,6 +133,7 @@ export type HostConfig<T, P, I, TI, PI, C, CX, PL> = {
133
type: T,
134
props: P,
135
rootContainerInstance: C,
136
+ hostContext: CX,
137
internalInstanceHandle: OpaqueHandle,
138
) => null | PL,
139
hydrateTextInstance?: (
src/renderers/shared/fiber/ReactFiberScheduler.js
+1
-1
@@ -152,7 +152,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
152
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
153
) {
154
const hostContext = ReactFiberHostContext(config);
155
- const hydrationContext: HydrationContext<C> = ReactFiberHydrationContext(
155
+ const hydrationContext: HydrationContext<C, CX> = ReactFiberHydrationContext(
156
config,
157
);
158
const {popHostContainer, popHostContext, resetHostContainer} = hostContext;