Files
react/src/renderers/dom/shared/ReactDOMTextComponent.js
T
Ben Alpert 622db4ee4f Fetch node to unmount separately from unmounting
My last strategy of getting each node recursively while unmounting was a pain to make work properly with ReactMount's confusing cache. Now, we get the node before unmounting anything in the subtree (and we don't try to find the nodes of descendants).

This is a temporary solution and can go away when we get rid of the giant ReactMount node hash map.

Fixes #5151.
2015-10-13 12:07:57 -07:00

161 lines
4.8 KiB
JavaScript

/**
* Copyright 2013-2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactDOMTextComponent
* @typechecks static-only
*/
'use strict';
var DOMChildrenOperations = require('DOMChildrenOperations');
var DOMPropertyOperations = require('DOMPropertyOperations');
var ReactComponentBrowserEnvironment =
require('ReactComponentBrowserEnvironment');
var ReactMount = require('ReactMount');
var assign = require('Object.assign');
var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
var setTextContent = require('setTextContent');
var validateDOMNesting = require('validateDOMNesting');
function getNode(inst) {
if (inst._nativeNode) {
return inst._nativeNode;
} else {
return inst._nativeNode = ReactMount.getNode(inst._rootNodeID);
}
}
/**
* Text nodes violate a couple assumptions that React makes about components:
*
* - When mounting text into the DOM, adjacent text nodes are merged.
* - Text nodes cannot be assigned a React root ID.
*
* This component is used to wrap strings in elements so that they can undergo
* the same reconciliation that is applied to elements.
*
* TODO: Investigate representing React components in the DOM with text nodes.
*
* @class ReactDOMTextComponent
* @extends ReactComponent
* @internal
*/
var ReactDOMTextComponent = function(props) {
// This constructor and its argument is currently used by mocks.
};
assign(ReactDOMTextComponent.prototype, {
/**
* @param {ReactText} text
* @internal
*/
construct: function(text) {
// TODO: This is really a ReactText (ReactNode), not a ReactElement
this._currentElement = text;
this._stringText = '' + text;
this._nativeNode = null;
// Properties
this._rootNodeID = null;
this._mountIndex = 0;
},
/**
* Creates the markup for this text node. This node is not intended to have
* any features besides containing text content.
*
* @param {string} rootID DOM ID of the root node.
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
* @return {string} Markup for this text node.
* @internal
*/
mountComponent: function(
rootID,
transaction,
nativeParent,
nativeContainerInfo,
context
) {
if (__DEV__) {
var parentInfo;
if (nativeParent != null) {
parentInfo = nativeParent._ancestorInfo;
} else if (nativeContainerInfo != null) {
parentInfo = nativeContainerInfo._ancestorInfo;
}
if (parentInfo) {
// parentInfo should always be present except for the top-level
// component when server rendering
validateDOMNesting(this._tag, this, parentInfo);
}
}
this._rootNodeID = rootID;
if (transaction.useCreateElement) {
var ownerDocument = nativeContainerInfo._ownerDocument;
var el = ownerDocument.createElement('span');
this._nativeNode = el;
DOMPropertyOperations.setAttributeForID(el, rootID);
// Populate node cache
ReactMount.getID(el);
setTextContent(el, this._stringText);
return el;
} else {
var escapedText = escapeTextContentForBrowser(this._stringText);
if (transaction.renderToStaticMarkup) {
// Normally we'd wrap this in a `span` for the reasons stated above, but
// since this is a situation where React won't take over (static pages),
// we can simply return the text as it is.
return escapedText;
}
return (
'<span ' + DOMPropertyOperations.createMarkupForID(rootID) + '>' +
escapedText +
'</span>'
);
}
},
/**
* Updates this component by updating the text content.
*
* @param {ReactText} nextText The next text content
* @param {ReactReconcileTransaction} transaction
* @internal
*/
receiveComponent: function(nextText, transaction) {
if (nextText !== this._currentElement) {
this._currentElement = nextText;
var nextStringText = '' + nextText;
if (nextStringText !== this._stringText) {
// TODO: Save this as pending props and use performUpdateIfNecessary
// and/or updateComponent to do the actual update for consistency with
// other component types?
this._stringText = nextStringText;
DOMChildrenOperations.updateTextContent(getNode(this), nextStringText);
}
}
},
getNativeNode: function() {
return getNode(this);
},
unmountComponent: function() {
this._nativeNode = null;
ReactComponentBrowserEnvironment.unmountIDFromEnvironment(this._rootNodeID);
},
});
module.exports = ReactDOMTextComponent;