-
Notifications
You must be signed in to change notification settings - Fork 46.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Fiber] Nesting validation warnings #8586
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,6 +39,15 @@ var { | |
} = ReactDOMFiberComponent; | ||
var { precacheFiberNode } = ReactDOMComponentTree; | ||
|
||
if (__DEV__) { | ||
var { | ||
getChildAncestorInfo, | ||
validateElementNesting, | ||
validateTextNesting, | ||
validateInlineTextNesting, | ||
} = ReactDOMFiberComponent; | ||
} | ||
|
||
const DOCUMENT_NODE = 9; | ||
|
||
ReactDOMInjection.inject(); | ||
|
@@ -61,9 +70,29 @@ let selectionInformation : ?mixed = null; | |
|
||
var DOMRenderer = ReactFiberReconciler({ | ||
|
||
getChildHostContext(parentHostContext : string | null, type : string) { | ||
const parentNamespace = parentHostContext; | ||
return getChildNamespace(parentNamespace, type); | ||
getRootHostContext(rootContainerInstance) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One benefit of this is that we should be able to mount SVG content into an |
||
const type = rootContainerInstance.tagName.toLowerCase(); | ||
if (__DEV__) { | ||
const namespace = getChildNamespace(null, type); | ||
const isMountingIntoDocument = rootContainerInstance.ownerDocument.documentElement === rootContainerInstance; | ||
const ancestorInfo = getChildAncestorInfo(null, isMountingIntoDocument ? '#document' : type); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does the child nesting validation not case about the namespace? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch. It doesn't (in Stack neither), and this is a bug. Because that should be valid: https://jsfiddle.net/q7e61jpd/. |
||
return {namespace, ancestorInfo}; | ||
} else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: We don't really use |
||
return getChildNamespace(null, type); | ||
} | ||
}, | ||
|
||
getChildHostContext( | ||
parentHostContext : string, | ||
type : string, | ||
) { | ||
if (__DEV__) { | ||
const namespace = getChildNamespace(parentHostContext.namespace, type); | ||
const ancestorInfo = getChildAncestorInfo(parentHostContext.ancestorInfo, type); | ||
return {namespace, ancestorInfo}; | ||
} else { | ||
return getChildNamespace(parentHostContext, type); | ||
} | ||
}, | ||
|
||
prepareForCommit() : void { | ||
|
@@ -86,7 +115,20 @@ var DOMRenderer = ReactFiberReconciler({ | |
hostContext : string | null, | ||
internalInstanceHandle : Object, | ||
) : Instance { | ||
const domElement : Instance = createElement(type, props, rootContainerInstance, hostContext); | ||
let parentNamespace; | ||
if (__DEV__) { | ||
validateElementNesting(hostContext.ancestorInfo, type); | ||
if ( | ||
typeof props.children === 'string' || | ||
typeof props.children === 'number' | ||
) { | ||
validateInlineTextNesting(hostContext.ancestorInfo, type, String(props.children)); | ||
} | ||
parentNamespace = hostContext.namespace; | ||
} else { | ||
parentNamespace = hostContext; | ||
} | ||
const domElement : Instance = createElement(type, props, rootContainerInstance, parentNamespace); | ||
precacheFiberNode(internalInstanceHandle, domElement); | ||
return domElement; | ||
}, | ||
|
@@ -108,8 +150,17 @@ var DOMRenderer = ReactFiberReconciler({ | |
domElement : Instance, | ||
type : string, | ||
oldProps : Props, | ||
newProps : Props | ||
newProps : Props, | ||
hostContext : string | ||
) : boolean { | ||
if (__DEV__) { | ||
if (oldProps.children !== newProps.children && ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I should probably check that we're moving to having a non-reified child here instead of always running the check. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also ^^^ the day I started saying "reified". What have you done to me. |
||
typeof newProps.children === 'string' || | ||
typeof newProps.children === 'number' | ||
)) { | ||
validateInlineTextNesting(hostContext.ancestorInfo, type, String(newProps.children)); | ||
} | ||
} | ||
return true; | ||
}, | ||
|
||
|
@@ -143,7 +194,15 @@ var DOMRenderer = ReactFiberReconciler({ | |
domElement.textContent = ''; | ||
}, | ||
|
||
createTextInstance(text : string, rootContainerInstance : Container, internalInstanceHandle : Object) : TextInstance { | ||
createTextInstance( | ||
text : string, | ||
rootContainerInstance : Container, | ||
hostContext, | ||
internalInstanceHandle : Object | ||
) : TextInstance { | ||
if (__DEV__) { | ||
validateTextNesting(hostContext.ancestorInfo, text); | ||
} | ||
var textNode : TextInstance = document.createTextNode(text); | ||
precacheFiberNode(internalInstanceHandle, textNode); | ||
return textNode; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,6 +39,8 @@ if (__DEV__) { | |
var ReactDOMInvalidARIAHook = require('ReactDOMInvalidARIAHook'); | ||
var ReactDOMNullInputValuePropHook = require('ReactDOMNullInputValuePropHook'); | ||
var ReactDOMUnknownPropertyHook = require('ReactDOMUnknownPropertyHook'); | ||
var validateDOMNesting = require('validateDOMNesting'); | ||
var { updatedAncestorInfo } = validateDOMNesting; | ||
var { validateProperties: validateARIAProperties } = ReactDOMInvalidARIAHook; | ||
var { validateProperties: validateInputPropertes } = ReactDOMNullInputValuePropHook; | ||
var { validateProperties: validateUnknownPropertes } = ReactDOMUnknownPropertyHook; | ||
|
@@ -649,7 +651,7 @@ var ReactDOMFiberComponent = { | |
tag : string, | ||
lastRawProps : Object, | ||
nextRawProps : Object, | ||
rootContainerElement : Element | ||
rootContainerElement : Element, | ||
) : void { | ||
if (__DEV__) { | ||
validatePropertiesInDevelopment(tag, nextRawProps); | ||
|
@@ -731,4 +733,20 @@ var ReactDOMFiberComponent = { | |
|
||
}; | ||
|
||
if (__DEV__) { | ||
ReactDOMFiberComponent.getChildAncestorInfo = function(parentAncestorInfo, type) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like these forwarding things. We have so much of these in the event system that just adds bloat, add risk for cyclic dependencies and confusion. Do you have a plan with these or can you just move this to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Btw, the only reason for a separate ReactDOMFiberComponent file is to keep parity with Stack so that we can easier replicate changes. Should get rid of this file later. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right. I thought I'd need them inside but then turned out I didn't. Will remove. |
||
return updatedAncestorInfo(parentAncestorInfo, type, null); | ||
}; | ||
ReactDOMFiberComponent.validateElementNesting = function(parentAncestorInfo, type) { | ||
validateDOMNesting(type, null, null, parentAncestorInfo); | ||
}; | ||
ReactDOMFiberComponent.validateTextNesting = function(parentAncestorInfo, text) { | ||
validateDOMNesting(null, text, null, parentAncestorInfo); | ||
}; | ||
ReactDOMFiberComponent.validateInlineTextNesting = function(parentAncestorInfo, type, text) { | ||
const ownAncestorInfo = updatedAncestorInfo(parentAncestorInfo, type, null); | ||
validateDOMNesting(null, text, null, ownAncestorInfo); | ||
}; | ||
} | ||
|
||
module.exports = ReactDOMFiberComponent; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,25 +15,20 @@ | |
var React = require('React'); | ||
|
||
var warning = require('warning'); | ||
var didWarnInvalidOptionChildren = false; | ||
|
||
function flattenChildren(children) { | ||
var content = ''; | ||
|
||
// Flatten children and warn if they aren't strings or numbers; | ||
// invalid types are ignored. | ||
// We can silently skip them because invalid DOM nesting warning | ||
// catches these cases in Fiber. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was a bit easier to keep nesting checks in |
||
React.Children.forEach(children, function(child) { | ||
if (child == null) { | ||
return; | ||
} | ||
if (typeof child === 'string' || typeof child === 'number') { | ||
content += child; | ||
} else if (!didWarnInvalidOptionChildren) { | ||
didWarnInvalidOptionChildren = true; | ||
warning( | ||
false, | ||
'Only strings and numbers are supported as <option> children.' | ||
); | ||
} | ||
}); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC you wanted something like this anyway.