diff --git a/src/renderers/dom/stack/client/DOMChildrenOperations.js b/src/renderers/dom/stack/client/DOMChildrenOperations.js index 2200f2529ad6b..16b503219dca8 100644 --- a/src/renderers/dom/stack/client/DOMChildrenOperations.js +++ b/src/renderers/dom/stack/client/DOMChildrenOperations.js @@ -13,6 +13,7 @@ var DOMLazyTree = require('DOMLazyTree'); var Danger = require('Danger'); +var warning = require('warning'); var ReactDOMComponentTree = require('ReactDOMComponentTree'); var ReactInstrumentation = require('ReactInstrumentation'); @@ -20,6 +21,8 @@ var createMicrosoftUnsafeLocalFunction = require('createMicrosoftUnsafeLocalFunc var setInnerHTML = require('setInnerHTML'); var setTextContent = require('setTextContent'); + + function getNodeAfter(parentNode, node) { // Special case for text components, which return [open, close] comments // from getHostNode. @@ -246,6 +249,32 @@ var DOMChildrenOperations = { } }, + /** + * Warns when React DOM is mutated by an external source. + * + * @return {object} observer instance with pre-defined options. + */ + mutationObserver: function() { + if (window.MutationObserver) { + var config = { + childList: true, + subtree: true, + }; + + // eslint-disable-next-line no-inner-declarations + function callback(mutations) { + warning(false, 'Warning, an outside source is mutating the DOM'); + } + + var observer = new MutationObserver(callback); + var reactRoots = document.querySelectorAll('[data-reactroot]'); + + for (let i = 0; i < reactRoots.length; i++) { + observer.observe(reactRoots[i], config); + } + return observer; + } + }, }; module.exports = DOMChildrenOperations; diff --git a/src/renderers/dom/stack/client/ReactMount.js b/src/renderers/dom/stack/client/ReactMount.js index 4186e0697067c..bfee5c980c31b 100644 --- a/src/renderers/dom/stack/client/ReactMount.js +++ b/src/renderers/dom/stack/client/ReactMount.js @@ -16,6 +16,7 @@ var DOMProperty = require('DOMProperty'); var React = require('React'); var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); var ReactCurrentOwner = require('ReactCurrentOwner'); +var DOMChildrenOperations = require('DOMChildrenOperations'); var ReactDOMComponentTree = require('ReactDOMComponentTree'); var ReactDOMContainerInfo = require('ReactDOMContainerInfo'); var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); @@ -131,6 +132,10 @@ function mountComponentIntoNode( shouldReuseMarkup, transaction ); + + if (__DEV__) { + DOMChildrenOperations.mutationObserver(); + } } /**