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
This commit is contained in:
Dan Abramov
2017-09-13 16:31:40 +01:00
committed by GitHub
parent 65b9ad94aa
commit c55ffb37f1
8 changed files with 63 additions and 18 deletions
@@ -776,6 +776,7 @@ var ReactDOMFiberComponent = {
domElement: Element,
tag: string,
rawProps: Object,
parentNamespace: string,
rootContainerElement: Element | Document,
): null | Array<mixed> {
if (__DEV__) {
@@ -912,6 +913,8 @@ var ReactDOMFiberComponent = {
case 'selected':
break;
default:
// Intentionally use the original name.
// See discussion in https://github.com/facebook/react/pull/10676.
extraAttributeNames.add(attributes[i].name);
}
}
@@ -1007,8 +1010,17 @@ var ReactDOMFiberComponent = {
nextProp,
);
} else {
// $FlowFixMe - Should be inferred as not undefined.
extraAttributeNames.delete(propKey.toLowerCase());
let ownNamespace = parentNamespace;
if (ownNamespace === HTML_NAMESPACE) {
ownNamespace = getIntrinsicNamespace(tag);
}
if (ownNamespace === HTML_NAMESPACE) {
// $FlowFixMe - Should be inferred as not undefined.
extraAttributeNames.delete(propKey.toLowerCase());
} else {
// $FlowFixMe - Should be inferred as not undefined.
extraAttributeNames.delete(propKey);
}
serverValue = DOMPropertyOperations.getValueForAttribute(
domElement,
propKey,
+15 -1
View File
@@ -488,13 +488,27 @@ var DOMRenderer = ReactFiberReconciler({
type: string,
props: Props,
rootContainerInstance: Container,
hostContext: HostContext,
internalInstanceHandle: Object,
): null | Array<mixed> {
precacheFiberNode(internalInstanceHandle, instance);
// TODO: Possibly defer this until the commit phase where all the events
// get attached.
updateFiberProps(instance, props);
return diffHydratedProperties(instance, type, props, rootContainerInstance);
let parentNamespace: string;
if (__DEV__) {
const hostContextDev = ((hostContext: any): HostContextDev);
parentNamespace = hostContextDev.namespace;
} else {
parentNamespace = ((hostContext: any): HostContextProd);
}
return diffHydratedProperties(
instance,
type,
props,
parentNamespace,
rootContainerInstance,
);
},
hydrateTextInstance(
@@ -1325,19 +1325,30 @@ describe('ReactDOMServerIntegration', () => {
expect(e.namespaceURI).toBe('http://www.w3.org/2000/svg');
});
itRenders('svg child element', async render => {
let e = await render(
<svg><image xlinkHref="http://i.imgur.com/w7GCRPb.png" /></svg>,
);
e = e.firstChild;
itRenders('svg child element with an attribute', async render => {
let e = await render(<svg viewBox="0 0 0 0" />);
expect(e.childNodes.length).toBe(0);
expect(e.tagName).toBe('image');
expect(e.tagName).toBe('svg');
expect(e.namespaceURI).toBe('http://www.w3.org/2000/svg');
expect(e.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBe(
'http://i.imgur.com/w7GCRPb.png',
);
expect(e.getAttribute('viewBox')).toBe('0 0 0 0');
});
itRenders(
'svg child element with a namespace attribute',
async render => {
let e = await render(
<svg><image xlinkHref="http://i.imgur.com/w7GCRPb.png" /></svg>,
);
e = e.firstChild;
expect(e.childNodes.length).toBe(0);
expect(e.tagName).toBe('image');
expect(e.namespaceURI).toBe('http://www.w3.org/2000/svg');
expect(e.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBe(
'http://i.imgur.com/w7GCRPb.png',
);
},
);
itRenders('svg child element with a badly cased alias', async render => {
let e = await render(
<svg><image xlinkhref="http://i.imgur.com/w7GCRPb.png" /></svg>,
@@ -72,7 +72,7 @@ if (__DEV__) {
module.exports = function<T, P, I, TI, PI, C, CX, PL>(
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
hostContext: HostContext<C, CX>,
hydrationContext: HydrationContext<C>,
hydrationContext: HydrationContext<C, CX>,
scheduleUpdate: (fiber: Fiber, priorityLevel: PriorityLevel) => void,
getPriorityContext: (fiber: Fiber, forceAsync: boolean) => PriorityLevel,
) {
@@ -53,7 +53,7 @@ var invariant = require('fbjs/lib/invariant');
module.exports = function<T, P, I, TI, PI, C, CX, PL>(
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
hostContext: HostContext<C, CX>,
hydrationContext: HydrationContext<C>,
hydrationContext: HydrationContext<C, CX>,
) {
const {
createInstance,
@@ -289,6 +289,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
prepareToHydrateHostInstance(
workInProgress,
rootContainerInstance,
currentHostContext,
)
) {
// If changes to the hydrated node needs to be applied at the
@@ -22,18 +22,22 @@ const {Deletion, Placement} = require('ReactTypeOfSideEffect');
const {createFiberFromHostInstanceForDeletion} = require('ReactFiber');
export type HydrationContext<C> = {
export type HydrationContext<C, CX> = {
enterHydrationState(fiber: Fiber): boolean,
resetHydrationState(): void,
tryToClaimNextHydratableInstance(fiber: Fiber): void,
prepareToHydrateHostInstance(fiber: Fiber, rootContainerInstance: C): boolean,
prepareToHydrateHostInstance(
fiber: Fiber,
rootContainerInstance: C,
hostContext: CX,
): boolean,
prepareToHydrateHostTextInstance(fiber: Fiber): boolean,
popHydrationState(fiber: Fiber): boolean,
};
module.exports = function<T, P, I, TI, PI, C, CX, PL>(
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
): HydrationContext<C> {
): HydrationContext<C, CX> {
const {
shouldSetTextContent,
canHydrateInstance,
@@ -218,6 +222,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
function prepareToHydrateHostInstance(
fiber: Fiber,
rootContainerInstance: C,
hostContext: CX,
): boolean {
const instance: I = fiber.stateNode;
const updatePayload = hydrateInstance(
@@ -225,6 +230,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
fiber.type,
fiber.memoizedProps,
rootContainerInstance,
hostContext,
fiber,
);
// TODO: Type this specific to this type of component.
@@ -133,6 +133,7 @@ export type HostConfig<T, P, I, TI, PI, C, CX, PL> = {
type: T,
props: P,
rootContainerInstance: C,
hostContext: CX,
internalInstanceHandle: OpaqueHandle,
) => null | PL,
hydrateTextInstance?: (
@@ -152,7 +152,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
) {
const hostContext = ReactFiberHostContext(config);
const hydrationContext: HydrationContext<C> = ReactFiberHydrationContext(
const hydrationContext: HydrationContext<C, CX> = ReactFiberHydrationContext(
config,
);
const {popHostContainer, popHostContext, resetHostContainer} = hostContext;