From eefda9377c7b734ceba413a3173cb48a357c04ae Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Wed, 17 Jun 2015 20:29:45 -0700 Subject: [PATCH] Add legacy methods to DOM components for compatibility --- src/renderers/dom/client/ReactMount.js | 6 +- src/renderers/dom/shared/ReactDOMComponent.js | 156 +++++++++++++++++- .../__tests__/ReactDOMComponent-test.js | 65 +++++++- .../reconciler/ReactCompositeComponent.js | 3 +- .../shared/reconciler/ReactUpdateQueue.js | 40 +++-- .../__tests__/ReactComponent-test.js | 9 +- 6 files changed, 245 insertions(+), 34 deletions(-) diff --git a/src/renderers/dom/client/ReactMount.js b/src/renderers/dom/client/ReactMount.js index 307f9123b1..ad4820b6b5 100644 --- a/src/renderers/dom/client/ReactMount.js +++ b/src/renderers/dom/client/ReactMount.js @@ -274,11 +274,7 @@ function mountComponentIntoNode( var markup = ReactReconciler.mountComponent( componentInstance, rootID, transaction, context ); - if (typeof componentInstance._renderedComponent._currentElement.type === - 'function') { - // hax - componentInstance._renderedComponent._isTopLevel = true; - } + componentInstance._renderedComponent._topLevelWrapper = componentInstance; ReactMount._mountImageIntoNode(markup, container, shouldReuseMarkup); } diff --git a/src/renderers/dom/shared/ReactDOMComponent.js b/src/renderers/dom/shared/ReactDOMComponent.js index 29023321ea..e4d7fe6bf1 100644 --- a/src/renderers/dom/shared/ReactDOMComponent.js +++ b/src/renderers/dom/shared/ReactDOMComponent.js @@ -30,6 +30,7 @@ var ReactDOMTextarea = require('ReactDOMTextarea'); var ReactMount = require('ReactMount'); var ReactMultiChild = require('ReactMultiChild'); var ReactPerf = require('ReactPerf'); +var ReactUpdateQueue = require('ReactUpdateQueue'); var assign = require('Object.assign'); var escapeTextContentForBrowser = require('escapeTextContentForBrowser'); @@ -51,6 +52,122 @@ var STYLE = keyOf({style: null}); var ELEMENT_NODE_TYPE = 1; +var canDefineProperty = false; +try { + Object.defineProperty({}, 'test', {get: function() {}}); + canDefineProperty = true; +} catch (e) { +} + +function getDeclarationErrorAddendum(internalInstance) { + if (internalInstance) { + var owner = internalInstance._currentElement._owner || null; + if (owner) { + var name = owner.getName(); + if (name) { + return ' This DOM node was rendered by `' + name + '`.'; + } + } + } + return ''; +} + +var legacyPropsDescriptor; +if (__DEV__) { + legacyPropsDescriptor = { + props: { + enumerable: false, + get: function() { + var component = this._reactInternalComponent; + warning( + false, + 'ReactDOMComponent: Do not access .props of a DOM node; instead, ' + + 'recreate the props as `render` did originally or read the DOM ' + + 'properties/attributes directly from this node (e.g., ' + + 'this.refs.box.className).%s', + getDeclarationErrorAddendum(component) + ); + return component._currentElement.props; + }, + }, + }; +} + +function legacyGetDOMNode() { + if (__DEV__) { + var component = this._reactInternalComponent; + warning( + false, + 'ReactDOMComponent: Do not access .getDOMNode() of a DOM node; ' + + 'instead, use the node directly.%s', + getDeclarationErrorAddendum(component) + ); + } + return this; +} + +function legacyIsMounted() { + var component = this._reactInternalComponent; + if (__DEV__) { + warning( + false, + 'ReactDOMComponent: Do not access .isMounted() of a DOM node.%s', + getDeclarationErrorAddendum(component) + ); + } + return !!component; +} + +function legacySetStateEtc() { + if (__DEV__) { + var component = this._reactInternalComponent; + warning( + false, + 'ReactDOMComponent: Do not access .setState(), .replaceState(), or ' + + '.forceUpdate() of a DOM node. This is a no-op.%s', + getDeclarationErrorAddendum(component) + ); + } +} + +function legacySetProps(partialProps, callback) { + var component = this._reactInternalComponent; + if (__DEV__) { + warning( + false, + 'ReactDOMComponent: Do not access .setProps() of a DOM node. ' + + 'Instead, call React.render again at the top level.%s', + getDeclarationErrorAddendum(component) + ); + } + if (!component) { + return; + } + ReactUpdateQueue.enqueueSetPropsInternal(component, partialProps); + if (callback) { + ReactUpdateQueue.enqueueCallbackInternal(component, callback); + } +} + +function legacyReplaceProps(partialProps, callback) { + var component = this._reactInternalComponent; + if (__DEV__) { + warning( + false, + 'ReactDOMComponent: Do not access .replaceProps() of a DOM node. ' + + 'Instead, call React.render again at the top level.%s', + getDeclarationErrorAddendum(component) + ); + } + if (!component) { + return; + } + ReactUpdateQueue.enqueueReplacePropsInternal(component, partialProps); + if (callback) { + ReactUpdateQueue.enqueueCallbackInternal(component, callback); + } +} + var styleMutationWarning = {}; function checkAndWarnForMutatedStyle(style1, style2, component) { @@ -327,6 +444,8 @@ function ReactDOMComponent(tag) { this._previousStyleCopy = null; this._rootNodeID = null; this._wrapperState = null; + this._topLevelWrapper = null; + this._nodeWithLegacyProperties = null; } ReactDOMComponent.displayName = 'ReactDOMComponent'; @@ -589,6 +708,10 @@ ReactDOMComponent.Mixin = { processChildContext(context, this) ); + if (!canDefineProperty && this._nodeWithLegacyProperties) { + this._nodeWithLegacyProperties.props = nextProps; + } + if (this._tag === 'select') { //