Remove Stack (part 1, safe: unused files and tests) (#10794)

* Remove Fiber Jest project

* Remove Stack reconciler and ReactDOMStack code

* Fix tests depending on Stack internals

* Fix Flow
This commit is contained in:
Dan Abramov
2017-09-27 15:15:20 +01:00
committed by GitHub
parent 6955414465
commit aad0970192
64 changed files with 71 additions and 9438 deletions
+35 -4
View File
@@ -108,9 +108,40 @@
"version-check": "node ./scripts/tasks/version-check.js"
},
"jest": {
"projects": [
"<rootDir>/scripts/jest/stack.config.json",
"<rootDir>/scripts/jest/fiber.config.json"
]
"modulePathIgnorePatterns": [
"/.module-cache/",
"<rootDir>/build/",
"<rootDir>/scripts/rollup/shims/",
"<rootDir>/scripts/bench/"
],
"transform": {
".*": "./scripts/jest/preprocessor.js"
},
"setupFiles": [
"./scripts/jest/setup.js",
"./scripts/jest/environment.js"
],
"setupTestFrameworkScriptFile": "./scripts/jest/test-framework-setup.js",
"testRegex": "/__tests__/.*(\\.js|coffee|ts)$",
"moduleFileExtensions": [
"js",
"json",
"node",
"coffee",
"ts"
],
"roots": [
"<rootDir>/eslint-rules",
"<rootDir>/mocks",
"<rootDir>/scripts",
"<rootDir>/src",
"node_modules/fbjs"
],
"collectCoverageFrom": [
"src/**/*.js",
"!src/__mocks__/vendor/third_party/*.js",
"!src/test/*.js"
],
"timers": "fake"
}
}
-38
View File
@@ -1,38 +0,0 @@
{
"modulePathIgnorePatterns": [
"/.module-cache/",
"<rootDir>/build/",
"<rootDir>/scripts/rollup/shims/",
"<rootDir>/scripts/bench/"
],
"rootDir": "../../",
"transform": {
".*": "./scripts/jest/preprocessor.js"
},
"setupFiles": [
"./scripts/jest/fiber.setup.js",
"./scripts/jest/environment.js"
],
"setupTestFrameworkScriptFile": "./scripts/jest/test-framework-setup.js",
"testRegex": "/__tests__/.*(\\.js|coffee|ts)$",
"moduleFileExtensions": [
"js",
"json",
"node",
"coffee",
"ts"
],
"roots": [
"<rootDir>/eslint-rules",
"<rootDir>/mocks",
"<rootDir>/scripts",
"<rootDir>/src",
"node_modules/fbjs"
],
"collectCoverageFrom": [
"src/**/*.js",
"!src/__mocks__/vendor/third_party/*.js",
"!src/test/*.js"
],
"timers": "fake"
}
@@ -2,12 +2,6 @@
// We want to globally mock this but jest doesn't let us do that by default
// for a file that already exists. So we have to explicitly mock it.
jest.mock('ReactDOMFeatureFlags', () => {
const flags = require.requireActual('ReactDOMFeatureFlags');
return Object.assign({}, flags, {
useFiber: true,
});
});
jest.mock('ReactFeatureFlags', () => {
const flags = require.requireActual('ReactFeatureFlags');
return Object.assign({}, flags, {
-38
View File
@@ -1,38 +0,0 @@
{
"modulePathIgnorePatterns": [
"/.module-cache/",
"<rootDir>/build/",
"<rootDir>/scripts/rollup/shims/",
"<rootDir>/scripts/bench/"
],
"rootDir": "../../",
"transform": {
".*": "./scripts/jest/preprocessor.js"
},
"setupFiles": [
"./scripts/jest/stack.setup.js",
"./scripts/jest/environment.js"
],
"setupTestFrameworkScriptFile": "./scripts/jest/test-framework-setup.js",
"testRegex": "/__tests__/.*(\\.js|coffee|ts)$",
"moduleFileExtensions": [
"js",
"json",
"node",
"coffee",
"ts"
],
"roots": [
"<rootDir>/eslint-rules",
"<rootDir>/mocks",
"<rootDir>/scripts",
"<rootDir>/src",
"node_modules/fbjs"
],
"collectCoverageFrom": [
"src/**/*.js",
"!src/__mocks__/vendor/third_party/*.js",
"!src/test/*.js"
],
"timers": "fake"
}
-14
View File
@@ -1,14 +0,0 @@
'use strict';
// We want to globally mock this but jest doesn't let us do that by default
// for a file that already exists. So we have to explicitly mock it.
jest.mock('ReactDOMFeatureFlags', () => {
const flags = require.requireActual('ReactDOMFeatureFlags');
return Object.assign({}, flags, {
useFiber: false,
});
});
// Error logging varies between Fiber and Stack;
// Rather than fork dozens of tests, mock the error-logging file by default.
jest.mock('ReactFiberErrorLogger');
+1 -1
View File
@@ -25,7 +25,7 @@ const facebookWWWSrcDependencies = [
// these files need to be copied to the react-native build
const reactNativeSrcDependencies = [
// TODO: copy this to RN repository and delete from React
'src/renderers/shared/stack/PooledClass.js',
'src/renderers/native/PooledClass.js',
'src/renderers/shared/fiber/isomorphic/ReactTypes.js',
'src/renderers/native/ReactNativeTypes.js',
];
+1 -6
View File
@@ -6,9 +6,4 @@
'use strict';
var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
var useFiber = ReactDOMFeatureFlags.useFiber;
module.exports = useFiber
? require('ReactDOMFiberEntry')
: require('ReactDOMStackEntry');
module.exports = require('ReactDOMFiberEntry');
+1 -6
View File
@@ -6,9 +6,4 @@
'use strict';
var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
var useFiber = ReactDOMFeatureFlags.useFiber;
module.exports = useFiber
? require('ReactDOMServerBrowserEntry')
: require('ReactDOMServerStackEntry');
module.exports = require('ReactDOMServerBrowserEntry');
+1 -6
View File
@@ -6,9 +6,4 @@
'use strict';
var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
var useFiber = ReactDOMFeatureFlags.useFiber;
module.exports = useFiber
? require('ReactDOMServerNodeEntry')
: require('ReactDOMServerStackEntry');
module.exports = require('ReactDOMServerNodeEntry');
-85
View File
@@ -306,91 +306,6 @@ describe('ref swapping', () => {
});
});
describe('string refs between fiber and stack', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactTestUtils = require('react-dom/test-utils');
});
it('attaches, detaches from fiber component with stack layer', () => {
const ReactCurrentOwner = require('ReactCurrentOwner');
const ReactDOMStack = require('ReactDOMStackEntry');
const ReactDOMFiber = require('ReactDOMFiberEntry');
const ReactInstanceMap = require('ReactInstanceMap');
let layerMounted = false;
class A extends React.Component {
render() {
return <div />;
}
componentDidMount() {
// ReactLayeredComponentMixin sets ReactCurrentOwner manually
ReactCurrentOwner.current = ReactInstanceMap.get(this);
const span = <span ref="span" />;
ReactCurrentOwner.current = null;
ReactDOMStack.unstable_renderSubtreeIntoContainer(
this,
span,
(this._container = document.createElement('div')),
() => {
expect(this.refs.span.nodeName).toBe('SPAN');
layerMounted = true;
},
);
}
componentWillUnmount() {
ReactDOMStack.unmountComponentAtNode(this._container);
}
}
const container = document.createElement('div');
const a = ReactDOMFiber.render(<A />, container);
expect(a.refs.span).toBeTruthy();
ReactDOMFiber.unmountComponentAtNode(container);
expect(a.refs.span).toBe(undefined);
expect(layerMounted).toBe(true);
});
it('attaches, detaches from stack component with fiber layer', () => {
const ReactCurrentOwner = require('ReactCurrentOwner');
const ReactDOM = require('ReactDOMStackEntry');
const ReactDOMFiber = require('ReactDOMFiberEntry');
const ReactInstanceMap = require('ReactInstanceMap');
let layerMounted = false;
class A extends React.Component {
render() {
return <div />;
}
componentDidMount() {
// ReactLayeredComponentMixin sets ReactCurrentOwner manually
ReactCurrentOwner.current = ReactInstanceMap.get(this);
const span = <span ref="span" />;
ReactCurrentOwner.current = null;
ReactDOMFiber.unstable_renderSubtreeIntoContainer(
this,
span,
(this._container = document.createElement('div')),
() => {
expect(this.refs.span.nodeName).toBe('SPAN');
layerMounted = true;
},
);
}
componentWillUnmount() {
ReactDOMFiber.unmountComponentAtNode(this._container);
}
}
const container = document.createElement('div');
const a = ReactDOM.render(<A />, container);
expect(a.refs.span).toBeTruthy();
ReactDOM.unmountComponentAtNode(container);
expect(a.refs.span).toBe(undefined);
expect(layerMounted).toBe(true);
});
});
describe('root level refs', () => {
it('attaches and detaches root refs', () => {
var ReactDOM = require('react-dom');
@@ -1,35 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactDOMServerStackEntry
*/
'use strict';
var ReactServerRendering = require('ReactServerRendering');
var ReactVersion = require('ReactVersion');
require('ReactDOMInjection');
require('ReactDOMStackInjection');
var ReactDOMServerStack = {
renderToString: ReactServerRendering.renderToString,
renderToStaticMarkup: ReactServerRendering.renderToStaticMarkup,
version: ReactVersion,
};
if (__DEV__) {
var ReactInstrumentation = require('ReactInstrumentation');
var ReactDOMUnknownPropertyHook = require('ReactDOMUnknownPropertyHook');
var ReactDOMNullInputValuePropHook = require('ReactDOMNullInputValuePropHook');
var ReactDOMInvalidARIAHook = require('ReactDOMInvalidARIAHook');
ReactInstrumentation.debugTool.addHook(ReactDOMUnknownPropertyHook);
ReactInstrumentation.debugTool.addHook(ReactDOMNullInputValuePropHook);
ReactInstrumentation.debugTool.addHook(ReactDOMInvalidARIAHook);
}
module.exports = ReactDOMServerStack;
-168
View File
@@ -1,168 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactDOMStackEntry
*/
/* globals __REACT_DEVTOOLS_GLOBAL_HOOK__*/
'use strict';
require('checkReact');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactGenericBatching = require('ReactGenericBatching');
var ReactMount = require('ReactMount');
var ReactUpdates = require('ReactUpdates');
var ReactReconciler = require('ReactReconciler');
var ReactVersion = require('ReactVersion');
var findDOMNode = require('findDOMNode');
var getHostComponentFromComposite = require('getHostComponentFromComposite');
if (__DEV__) {
var warning = require('fbjs/lib/warning');
}
require('ReactDOMInjection');
require('ReactDOMClientInjection');
require('ReactDOMStackInjection');
var ReactDOMStack = {
findDOMNode: findDOMNode,
render: ReactMount.render,
unmountComponentAtNode: ReactMount.unmountComponentAtNode,
version: ReactVersion,
/* eslint-disable camelcase */
unstable_batchedUpdates: ReactGenericBatching.batchedUpdates,
unstable_renderSubtreeIntoContainer: ReactMount.renderSubtreeIntoContainer,
/* eslint-enable camelcase */
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
// For TapEventPlugin which is popular in open source
EventPluginHub: require('EventPluginHub'),
// Used by test-utils
EventPluginRegistry: require('EventPluginRegistry'),
EventPropagators: require('EventPropagators'),
ReactControlledComponent: require('ReactControlledComponent'),
ReactDOMComponentTree,
ReactDOMEventListener: require('ReactDOMEventListener'),
ReactUpdates: ReactUpdates,
},
};
// Inject the runtime into a devtools global hook regardless of browser.
// Allows for debugging when the hook is injected on the page.
if (
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function'
) {
__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({
ComponentTree: {
getClosestInstanceFromNode: ReactDOMComponentTree.getClosestInstanceFromNode,
getNodeFromInstance: function(inst) {
// inst is an internal instance (but could be a composite)
if (inst._renderedComponent) {
inst = getHostComponentFromComposite(inst);
}
if (inst) {
return ReactDOMComponentTree.getNodeFromInstance(inst);
} else {
return null;
}
},
},
Mount: ReactMount,
Reconciler: ReactReconciler,
rendererPackageName: 'react-dom',
});
}
if (__DEV__) {
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
if (ExecutionEnvironment.canUseDOM && window.top === window.self) {
// First check if devtools is not installed
if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') {
// If we're in Chrome or Firefox, provide a download link if not installed.
if (
(navigator.userAgent.indexOf('Chrome') > -1 &&
navigator.userAgent.indexOf('Edge') === -1) ||
navigator.userAgent.indexOf('Firefox') > -1
) {
// Firefox does not have the issue with devtools loaded over file://
var showFileUrlMessage =
window.location.protocol.indexOf('http') === -1 &&
navigator.userAgent.indexOf('Firefox') === -1;
console.debug(
'Download the React DevTools ' +
(showFileUrlMessage
? 'and use an HTTP server (instead of a file: URL) '
: '') +
'for a better development experience: ' +
'https://fb.me/react-devtools',
);
}
}
var testFunc = function testFn() {};
warning(
(testFunc.name || testFunc.toString()).indexOf('testFn') !== -1,
"It looks like you're using a minified copy of the development build " +
'of React. When deploying React apps to production, make sure to use ' +
'the production build which skips development warnings and is faster. ' +
'See https://fb.me/react-minification for more details.',
);
// If we're in IE8, check to see if we are in compatibility mode and provide
// information on preventing compatibility mode
var ieCompatibilityMode =
document.documentMode && document.documentMode < 8;
warning(
!ieCompatibilityMode,
'Internet Explorer is running in compatibility mode; please add the ' +
'following tag to your HTML to prevent this from happening: ' +
'<meta http-equiv="X-UA-Compatible" content="IE=edge" />',
);
var expectedFeatures = [
// shims
Array.isArray,
Array.prototype.every,
Array.prototype.forEach,
Array.prototype.indexOf,
Array.prototype.map,
Date.now,
Function.prototype.bind,
Object.keys,
String.prototype.trim,
];
for (var i = 0; i < expectedFeatures.length; i++) {
if (!expectedFeatures[i]) {
warning(
false,
'One or more ES5 shims expected by React are not available: ' +
'https://fb.me/react-warning-polyfills',
);
break;
}
}
}
}
if (__DEV__) {
var ReactInstrumentation = require('ReactInstrumentation');
var ReactDOMUnknownPropertyHook = require('ReactDOMUnknownPropertyHook');
var ReactDOMNullInputValuePropHook = require('ReactDOMNullInputValuePropHook');
var ReactDOMInvalidARIAHook = require('ReactDOMInvalidARIAHook');
ReactInstrumentation.debugTool.addHook(ReactDOMUnknownPropertyHook);
ReactInstrumentation.debugTool.addHook(ReactDOMNullInputValuePropHook);
ReactInstrumentation.debugTool.addHook(ReactDOMInvalidARIAHook);
}
module.exports = ReactDOMStack;
@@ -13,8 +13,6 @@ var ExecutionEnvironment;
var React;
var ReactDOM;
var ReactDOMServer;
var ReactMarkupChecksum;
var ReactReconcileTransaction;
var ReactTestUtils;
var PropTypes;
@@ -29,8 +27,6 @@ describe('ReactDOMServer', () => {
React = require('react');
ReactDOM = require('react-dom');
ReactTestUtils = require('react-dom/test-utils');
ReactMarkupChecksum = require('ReactMarkupChecksum');
ReactReconcileTransaction = require('ReactReconcileTransaction');
PropTypes = require('prop-types');
ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
@@ -47,16 +43,7 @@ describe('ReactDOMServer', () => {
var response = ReactDOMServer.renderToString(<span>hello world</span>);
expect(response).toMatch(
new RegExp(
'<span ' +
ROOT_ATTRIBUTE_NAME +
'=""' +
(ReactDOMFeatureFlags.useFiber
? ''
: ' ' + ID_ATTRIBUTE_NAME + '="[^"]*"') +
(ReactDOMFeatureFlags.useFiber
? ''
: ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
'>hello world</span>',
'<span ' + ROOT_ATTRIBUTE_NAME + '=""' + '>hello world</span>',
),
);
});
@@ -64,18 +51,7 @@ describe('ReactDOMServer', () => {
it('should generate simple markup for self-closing tags', () => {
var response = ReactDOMServer.renderToString(<img />);
expect(response).toMatch(
new RegExp(
'<img ' +
ROOT_ATTRIBUTE_NAME +
'=""' +
(ReactDOMFeatureFlags.useFiber
? ''
: ' ' + ID_ATTRIBUTE_NAME + '="[^"]*"') +
(ReactDOMFeatureFlags.useFiber
? ''
: ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
'/>',
),
new RegExp('<img ' + ROOT_ATTRIBUTE_NAME + '=""' + '/>'),
);
});
@@ -83,16 +59,7 @@ describe('ReactDOMServer', () => {
var response = ReactDOMServer.renderToString(<img data-attr=">" />);
expect(response).toMatch(
new RegExp(
'<img data-attr="&gt;" ' +
ROOT_ATTRIBUTE_NAME +
'=""' +
(ReactDOMFeatureFlags.useFiber
? ''
: ' ' + ID_ATTRIBUTE_NAME + '="[^"]*"') +
(ReactDOMFeatureFlags.useFiber
? ''
: ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
'/>',
'<img data-attr="&gt;" ' + ROOT_ATTRIBUTE_NAME + '=""' + '/>',
),
);
});
@@ -133,17 +100,8 @@ describe('ReactDOMServer', () => {
'<div ' +
ROOT_ATTRIBUTE_NAME +
'=""' +
(ReactDOMFeatureFlags.useFiber
? ''
: ' ' + ID_ATTRIBUTE_NAME + '="[^"]*"') +
(ReactDOMFeatureFlags.useFiber
? ''
: ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
'>' +
'<span' +
(ReactDOMFeatureFlags.useFiber
? ''
: ' ' + ID_ATTRIBUTE_NAME + '="[^"]*"') +
'>' +
(ReactDOMFeatureFlags.useFiber
? 'My name is <!-- -->child'
@@ -210,9 +168,6 @@ describe('ReactDOMServer', () => {
(ReactDOMFeatureFlags.useFiber
? ''
: ' ' + ID_ATTRIBUTE_NAME + '="[^"]*"') +
(ReactDOMFeatureFlags.useFiber
? ''
: ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
'>' +
(ReactDOMFeatureFlags.useFiber
? 'Component name: <!-- -->TestComponent'
@@ -576,11 +531,6 @@ describe('ReactDOMServer', () => {
return <div>{this.state.text}</div>;
}
}
ReactReconcileTransaction.prototype.perform = function() {
// We shouldn't ever be calling this on the server
throw new Error('Browser reconcile transaction should not be used');
};
var markup = ReactDOMServer.renderToString(<Component />);
expect(markup).toContain('hello, world');
});
@@ -600,11 +550,6 @@ describe('ReactDOMServer', () => {
return <div>{this.state.text}</div>;
}
}
ReactReconcileTransaction.prototype.perform = function() {
// We shouldn't ever be calling this on the server
throw new Error('Browser reconcile transaction should not be used');
};
var markup = ReactDOMServer.renderToString(<Component />);
expect(markup).toContain('hello, world');
});
@@ -1,248 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule DOMChildrenOperations
*/
'use strict';
var DOMLazyTree = require('DOMLazyTree');
var Danger = require('Danger');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactInstrumentation = require('ReactInstrumentation');
var createMicrosoftUnsafeLocalFunction = require('createMicrosoftUnsafeLocalFunction');
var setInnerHTML = require('setInnerHTML');
var setTextContent = require('setTextContent');
function getNodeAfter(parentNode, node) {
// Special case for text components, which return [open, close] comments
// from getHostNode.
if (Array.isArray(node)) {
node = node[1];
}
return node ? node.nextSibling : parentNode.firstChild;
}
/**
* Inserts `childNode` as a child of `parentNode` at the `index`.
*
* @param {DOMElement} parentNode Parent node in which to insert.
* @param {DOMElement} childNode Child node to insert.
* @param {number} index Index at which to insert the child.
* @internal
*/
var insertChildAt = createMicrosoftUnsafeLocalFunction(function(
parentNode,
childNode,
referenceNode,
) {
// We rely exclusively on `insertBefore(node, null)` instead of also using
// `appendChild(node)`. (Using `undefined` is not allowed by all browsers so
// we are careful to use `null`.)
parentNode.insertBefore(childNode, referenceNode);
});
function insertLazyTreeChildAt(parentNode, childTree, referenceNode) {
DOMLazyTree.insertTreeBefore(parentNode, childTree, referenceNode);
}
function moveChild(parentNode, childNode, referenceNode) {
if (Array.isArray(childNode)) {
moveDelimitedText(parentNode, childNode[0], childNode[1], referenceNode);
} else {
insertChildAt(parentNode, childNode, referenceNode);
}
}
function removeChild(parentNode, childNode) {
if (Array.isArray(childNode)) {
var closingComment = childNode[1];
childNode = childNode[0];
removeDelimitedText(parentNode, childNode, closingComment);
parentNode.removeChild(closingComment);
}
parentNode.removeChild(childNode);
}
function moveDelimitedText(
parentNode,
openingComment,
closingComment,
referenceNode,
) {
var node = openingComment;
while (true) {
var nextNode = node.nextSibling;
insertChildAt(parentNode, node, referenceNode);
if (node === closingComment) {
break;
}
node = nextNode;
}
}
function removeDelimitedText(parentNode, startNode, closingComment) {
while (true) {
var node = startNode.nextSibling;
if (node === closingComment) {
// The closing comment is removed by ReactMultiChild.
break;
} else {
parentNode.removeChild(node);
}
}
}
function replaceDelimitedText(openingComment, closingComment, stringText) {
var parentNode = openingComment.parentNode;
var nodeAfterComment = openingComment.nextSibling;
if (nodeAfterComment === closingComment) {
// There are no text nodes between the opening and closing comments; insert
// a new one if stringText isn't empty.
if (stringText) {
insertChildAt(
parentNode,
document.createTextNode(stringText),
nodeAfterComment,
);
}
} else {
if (stringText) {
// Set the text content of the first node after the opening comment, and
// remove all following nodes up until the closing comment.
setTextContent(nodeAfterComment, stringText);
removeDelimitedText(parentNode, nodeAfterComment, closingComment);
} else {
removeDelimitedText(parentNode, openingComment, closingComment);
}
}
if (__DEV__) {
ReactInstrumentation.debugTool.onHostOperation({
instanceID: ReactDOMComponentTree.getInstanceFromNode(openingComment)
._debugID,
type: 'replace text',
payload: stringText,
});
}
}
var dangerouslyReplaceNodeWithMarkup = Danger.dangerouslyReplaceNodeWithMarkup;
if (__DEV__) {
dangerouslyReplaceNodeWithMarkup = function(oldChild, markup, prevInstance) {
Danger.dangerouslyReplaceNodeWithMarkup(oldChild, markup);
if (prevInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onHostOperation({
instanceID: prevInstance._debugID,
type: 'replace with',
payload: markup.toString(),
});
} else {
var nextInstance = ReactDOMComponentTree.getInstanceFromNode(markup.node);
if (nextInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onHostOperation({
instanceID: nextInstance._debugID,
type: 'mount',
payload: markup.toString(),
});
}
}
};
}
/**
* Operations for updating with DOM children.
*/
var DOMChildrenOperations = {
dangerouslyReplaceNodeWithMarkup: dangerouslyReplaceNodeWithMarkup,
replaceDelimitedText: replaceDelimitedText,
/**
* Updates a component's children by processing a series of updates. The
* update configurations are each expected to have a `parentNode` property.
*
* @param {array<object>} updates List of update configurations.
* @internal
*/
processUpdates: function(parentNode, updates) {
if (__DEV__) {
var parentNodeDebugID = ReactDOMComponentTree.getInstanceFromNode(
parentNode,
)._debugID;
}
for (var k = 0; k < updates.length; k++) {
var update = updates[k];
switch (update.type) {
case 'INSERT_MARKUP':
insertLazyTreeChildAt(
parentNode,
update.content,
getNodeAfter(parentNode, update.afterNode),
);
if (__DEV__) {
ReactInstrumentation.debugTool.onHostOperation({
instanceID: parentNodeDebugID,
type: 'insert child',
payload: {
toIndex: update.toIndex,
content: update.content.toString(),
},
});
}
break;
case 'MOVE_EXISTING':
moveChild(
parentNode,
update.fromNode,
getNodeAfter(parentNode, update.afterNode),
);
if (__DEV__) {
ReactInstrumentation.debugTool.onHostOperation({
instanceID: parentNodeDebugID,
type: 'move child',
payload: {fromIndex: update.fromIndex, toIndex: update.toIndex},
});
}
break;
case 'SET_MARKUP':
setInnerHTML(parentNode, update.content);
if (__DEV__) {
ReactInstrumentation.debugTool.onHostOperation({
instanceID: parentNodeDebugID,
type: 'replace children',
payload: update.content.toString(),
});
}
break;
case 'TEXT_CONTENT':
setTextContent(parentNode, update.content);
if (__DEV__) {
ReactInstrumentation.debugTool.onHostOperation({
instanceID: parentNodeDebugID,
type: 'replace text',
payload: update.content.toString(),
});
}
break;
case 'REMOVE_NODE':
removeChild(parentNode, update.fromNode);
if (__DEV__) {
ReactInstrumentation.debugTool.onHostOperation({
instanceID: parentNodeDebugID,
type: 'remove child',
payload: {fromIndex: update.fromIndex},
});
}
break;
}
}
},
};
module.exports = DOMChildrenOperations;
@@ -1,128 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule DOMLazyTree
*/
'use strict';
var Namespaces = require('DOMNamespaces').Namespaces;
var setInnerHTML = require('setInnerHTML');
var {DOCUMENT_FRAGMENT_NODE, ELEMENT_NODE} = require('HTMLNodeType');
var createMicrosoftUnsafeLocalFunction = require('createMicrosoftUnsafeLocalFunction');
var setTextContent = require('setTextContent');
/**
* In IE (8-11) and Edge, appending nodes with no children is dramatically
* faster than appending a full subtree, so we essentially queue up the
* .appendChild calls here and apply them so each node is added to its parent
* before any children are added.
*
* In other browsers, doing so is slower or neutral compared to the other order
* (in Firefox, twice as slow) so we only do this inversion in IE.
*
* See https://github.com/sophiebits/innerhtml-vs-createelement-vs-clonenode.
*/
var enableLazy =
(typeof document !== 'undefined' &&
typeof document.documentMode === 'number') ||
(typeof navigator !== 'undefined' &&
typeof navigator.userAgent === 'string' &&
/\bEdge\/\d/.test(navigator.userAgent));
function insertTreeChildren(tree) {
if (!enableLazy) {
return;
}
var node = tree.node;
var children = tree.children;
if (children.length) {
for (var i = 0; i < children.length; i++) {
insertTreeBefore(node, children[i], null);
}
} else if (tree.html != null) {
setInnerHTML(node, tree.html);
} else if (tree.text != null) {
setTextContent(node, tree.text);
}
}
var insertTreeBefore = createMicrosoftUnsafeLocalFunction(function(
parentNode,
tree,
referenceNode,
) {
// DocumentFragments aren't actually part of the DOM after insertion so
// appending children won't update the DOM. We need to ensure the fragment
// is properly populated first, breaking out of our lazy approach for just
// this level. Also, some <object> plugins (like Flash Player) will read
// <param> nodes immediately upon insertion into the DOM, so <object>
// must also be populated prior to insertion into the DOM.
if (
tree.node.nodeType === DOCUMENT_FRAGMENT_NODE ||
(tree.node.nodeType === ELEMENT_NODE &&
tree.node.nodeName.toLowerCase() === 'object' &&
(tree.node.namespaceURI == null ||
tree.node.namespaceURI === Namespaces.html))
) {
insertTreeChildren(tree);
parentNode.insertBefore(tree.node, referenceNode);
} else {
parentNode.insertBefore(tree.node, referenceNode);
insertTreeChildren(tree);
}
});
function replaceChildWithTree(oldNode, newTree) {
oldNode.parentNode.replaceChild(newTree.node, oldNode);
insertTreeChildren(newTree);
}
function queueChild(parentTree, childTree) {
if (enableLazy) {
parentTree.children.push(childTree);
} else {
parentTree.node.appendChild(childTree.node);
}
}
function queueHTML(tree, html) {
if (enableLazy) {
tree.html = html;
} else {
setInnerHTML(tree.node, html);
}
}
function queueText(tree, text) {
if (enableLazy) {
tree.text = text;
} else {
setTextContent(tree.node, text);
}
}
function toString() {
return this.node.nodeName;
}
function DOMLazyTree(node) {
return {
node: node,
children: [],
html: null,
text: null,
toString,
};
}
DOMLazyTree.insertTreeBefore = insertTreeBefore;
DOMLazyTree.replaceChildWithTree = replaceChildWithTree;
DOMLazyTree.queueChild = queueChild;
DOMLazyTree.queueHTML = queueHTML;
DOMLazyTree.queueText = queueText;
module.exports = DOMLazyTree;
-54
View File
@@ -1,54 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Danger
*/
'use strict';
var DOMLazyTree = require('DOMLazyTree');
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
var createNodesFromMarkup = require('fbjs/lib/createNodesFromMarkup');
var emptyFunction = require('fbjs/lib/emptyFunction');
var invariant = require('fbjs/lib/invariant');
var Danger = {
/**
* Replaces a node with a string of markup at its current position within its
* parent. The markup must render into a single root node.
*
* @param {DOMElement} oldChild Child node to replace.
* @param {string} markup Markup to render in place of the child node.
* @internal
*/
dangerouslyReplaceNodeWithMarkup: function(oldChild, markup) {
invariant(
ExecutionEnvironment.canUseDOM,
'dangerouslyReplaceNodeWithMarkup(...): Cannot render markup in a ' +
'worker thread. Make sure `window` and `document` are available ' +
'globally before requiring React when unit testing or use ' +
'ReactDOMServer.renderToString() for server rendering.',
);
invariant(markup, 'dangerouslyReplaceNodeWithMarkup(...): Missing markup.');
invariant(
oldChild.nodeName !== 'HTML',
'dangerouslyReplaceNodeWithMarkup(...): Cannot replace markup of the ' +
'<html> node. This is because browser quirks make this unreliable ' +
'and/or slow. If you want to render to the root you must use ' +
'server rendering. See ReactDOMServer.renderToString().',
);
if (typeof markup === 'string') {
var newChild = createNodesFromMarkup(markup, emptyFunction)[0];
oldChild.parentNode.replaceChild(newChild, oldChild);
} else {
DOMLazyTree.replaceChildWithTree(oldChild, markup);
}
},
};
module.exports = Danger;
@@ -1,26 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactComponentBrowserEnvironment
*/
'use strict';
var DOMChildrenOperations = require('DOMChildrenOperations');
var ReactDOMIDOperations = require('ReactDOMIDOperations');
/**
* Abstracts away all functionality of the reconciler that requires knowledge of
* the browser context. TODO: These callers should be refactored to avoid the
* need for this injection.
*/
var ReactComponentBrowserEnvironment = {
processChildrenUpdates: ReactDOMIDOperations.dangerouslyProcessChildrenUpdates,
replaceNodeWithMarkup: DOMChildrenOperations.dangerouslyReplaceNodeWithMarkup,
};
module.exports = ReactComponentBrowserEnvironment;
File diff suppressed because it is too large Load Diff
@@ -1,34 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactDOMContainerInfo
*/
'use strict';
var validateDOMNesting = require('validateDOMNesting');
var {DOCUMENT_NODE} = require('HTMLNodeType');
function ReactDOMContainerInfo(topLevelWrapper, node) {
var info = {
_topLevelWrapper: topLevelWrapper,
_idCounter: 1,
_ownerDocument: node
? node.nodeType === DOCUMENT_NODE ? node : node.ownerDocument
: null,
_node: node,
_tag: node ? node.nodeName.toLowerCase() : null,
_namespaceURI: node ? node.namespaceURI : null,
};
if (__DEV__) {
info._ancestorInfo = node
? validateDOMNesting.updatedAncestorInfo(null, info._tag, null)
: null;
}
return info;
}
module.exports = ReactDOMContainerInfo;
@@ -1,61 +0,0 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactDOMEmptyComponent
*/
'use strict';
var DOMLazyTree = require('DOMLazyTree');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactDOMEmptyComponent = function(instantiate) {
// ReactCompositeComponent uses this:
this._currentElement = null;
// ReactDOMComponentTree uses these:
this._hostNode = null;
this._hostParent = null;
this._hostContainerInfo = null;
this._domID = 0;
};
Object.assign(ReactDOMEmptyComponent.prototype, {
mountComponent: function(
transaction,
hostParent,
hostContainerInfo,
context,
) {
var domID = hostContainerInfo._idCounter++;
this._domID = domID;
this._hostParent = hostParent;
this._hostContainerInfo = hostContainerInfo;
var nodeValue = ' react-empty: ' + this._domID + ' ';
if (transaction.useCreateElement) {
var ownerDocument = hostContainerInfo._ownerDocument;
var node = ownerDocument.createComment(nodeValue);
ReactDOMComponentTree.precacheNode(this, node);
return DOMLazyTree(node);
} else {
if (transaction.renderToStaticMarkup) {
// Normally we'd insert a comment node, but since this is a situation
// where React won't take over (static pages), we can simply return
// nothing.
return '';
}
return '<!--' + nodeValue + '-->';
}
},
receiveComponent: function() {},
getHostNode: function() {
return ReactDOMComponentTree.getNodeFromInstance(this);
},
unmountComponent: function() {
ReactDOMComponentTree.uncacheNode(this);
},
});
module.exports = ReactDOMEmptyComponent;
@@ -1,31 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactDOMIDOperations
*/
'use strict';
var DOMChildrenOperations = require('DOMChildrenOperations');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
/**
* Operations used to process updates to DOM nodes.
*/
var ReactDOMIDOperations = {
/**
* Updates a component's children by processing a series of updates.
*
* @param {array<object>} updates List of update configurations.
* @internal
*/
dangerouslyProcessChildrenUpdates: function(parentInst, updates) {
var node = ReactDOMComponentTree.getNodeFromInstance(parentInst);
DOMChildrenOperations.processUpdates(node, updates);
},
};
module.exports = ReactDOMIDOperations;
@@ -1,52 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactDOMStackInjection
*/
'use strict';
var ReactComponentEnvironment = require('ReactComponentEnvironment');
var ReactComponentBrowserEnvironment = require('ReactComponentBrowserEnvironment');
var ReactDOMComponent = require('ReactDOMComponent');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactDOMEmptyComponent = require('ReactDOMEmptyComponent');
var ReactDOMTextComponent = require('ReactDOMTextComponent');
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
var ReactEmptyComponent = require('ReactEmptyComponent');
var ReactGenericBatching = require('ReactGenericBatching');
var ReactHostComponent = require('ReactHostComponent');
var ReactReconcileTransaction = require('ReactReconcileTransaction');
var ReactUpdates = require('ReactUpdates');
var findDOMNode = require('findDOMNode');
var getHostComponentFromComposite = require('getHostComponentFromComposite');
ReactGenericBatching.injection.injectStackBatchedUpdates(
ReactUpdates.batchedUpdates,
);
ReactHostComponent.injection.injectGenericComponentClass(ReactDOMComponent);
ReactHostComponent.injection.injectTextComponentClass(ReactDOMTextComponent);
ReactEmptyComponent.injection.injectEmptyComponentFactory(function(
instantiate,
) {
return new ReactDOMEmptyComponent(instantiate);
});
ReactUpdates.injection.injectReconcileTransaction(ReactReconcileTransaction);
ReactUpdates.injection.injectBatchingStrategy(ReactDefaultBatchingStrategy);
ReactComponentEnvironment.injection.injectEnvironment(
ReactComponentBrowserEnvironment,
);
findDOMNode._injectStack(function(inst) {
inst = getHostComponentFromComposite(inst);
return inst ? ReactDOMComponentTree.getNodeFromInstance(inst) : null;
});
@@ -1,185 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactDOMTextComponent
*/
'use strict';
var DOMChildrenOperations = require('DOMChildrenOperations');
var DOMLazyTree = require('DOMLazyTree');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var {COMMENT_NODE} = require('HTMLNodeType');
var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
var invariant = require('fbjs/lib/invariant');
var validateDOMNesting = require('validateDOMNesting');
/**
* 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 between comment nodes 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(text) {
// TODO: This is really a ReactText (ReactNode), not a ReactElement
this._currentElement = text;
this._stringText = '' + text;
// ReactDOMComponentTree uses these:
this._hostNode = null;
this._hostParent = null;
// Properties
this._domID = 0;
this._mountIndex = 0;
this._closingComment = null;
this._commentNodes = null;
};
Object.assign(ReactDOMTextComponent.prototype, {
/**
* Creates the markup for this text node. This node is not intended to have
* any features besides containing text content.
*
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
* @return {string} Markup for this text node.
* @internal
*/
mountComponent: function(
transaction,
hostParent,
hostContainerInfo,
context,
) {
if (__DEV__) {
var parentInfo;
if (hostParent != null) {
parentInfo = hostParent._ancestorInfo;
} else if (hostContainerInfo != null) {
parentInfo = hostContainerInfo._ancestorInfo;
}
if (parentInfo) {
// parentInfo should always be present except for the top-level
// component when server rendering
validateDOMNesting(null, this._stringText, this, parentInfo);
}
}
var domID = hostContainerInfo._idCounter++;
var openingValue = ' react-text: ' + domID + ' ';
var closingValue = ' /react-text ';
this._domID = domID;
this._hostParent = hostParent;
if (transaction.useCreateElement) {
var ownerDocument = hostContainerInfo._ownerDocument;
var openingComment = ownerDocument.createComment(openingValue);
var closingComment = ownerDocument.createComment(closingValue);
var lazyTree = DOMLazyTree(ownerDocument.createDocumentFragment());
DOMLazyTree.queueChild(lazyTree, DOMLazyTree(openingComment));
if (this._stringText) {
DOMLazyTree.queueChild(
lazyTree,
DOMLazyTree(ownerDocument.createTextNode(this._stringText)),
);
}
DOMLazyTree.queueChild(lazyTree, DOMLazyTree(closingComment));
ReactDOMComponentTree.precacheNode(this, openingComment);
this._closingComment = closingComment;
return lazyTree;
} else {
var escapedText = escapeTextContentForBrowser(this._stringText);
if (transaction.renderToStaticMarkup) {
// Normally we'd wrap this between comment nodes 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 (
'<!--' +
openingValue +
'-->' +
escapedText +
'<!--' +
closingValue +
'-->'
);
}
},
/**
* 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;
var commentNodes = this.getHostNode();
DOMChildrenOperations.replaceDelimitedText(
commentNodes[0],
commentNodes[1],
nextStringText,
);
}
}
},
getHostNode: function() {
var hostNode = this._commentNodes;
if (hostNode) {
return hostNode;
}
if (!this._closingComment) {
var openingComment = ReactDOMComponentTree.getNodeFromInstance(this);
var node = openingComment.nextSibling;
while (true) {
invariant(
node != null,
'Missing closing comment for text component %s',
this._domID,
);
if (
node.nodeType === COMMENT_NODE &&
node.nodeValue === ' /react-text '
) {
this._closingComment = node;
break;
}
node = node.nextSibling;
}
}
hostNode = [this._hostNode, this._closingComment];
this._commentNodes = hostNode;
return hostNode;
},
unmountComponent: function() {
this._closingComment = null;
this._commentNodes = null;
ReactDOMComponentTree.uncacheNode(this);
},
});
module.exports = ReactDOMTextComponent;
@@ -1,778 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactMount
*/
'use strict';
var DOMLazyTree = require('DOMLazyTree');
var DOMProperty = require('DOMProperty');
var React = require('react');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactDOMContainerInfo = require('ReactDOMContainerInfo');
var ReactInstanceMap = require('ReactInstanceMap');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactMarkupChecksum = require('ReactMarkupChecksum');
var ReactReconciler = require('ReactReconciler');
var ReactUpdateQueue = require('ReactUpdateQueue');
var ReactUpdates = require('ReactUpdates');
var {ReactCurrentOwner} = require('ReactGlobalSharedState');
var getContextForSubtree = require('getContextForSubtree');
var instantiateReactComponent = require('instantiateReactComponent');
var invariant = require('fbjs/lib/invariant');
var setInnerHTML = require('setInnerHTML');
var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
var warning = require('fbjs/lib/warning');
var validateCallback = require('validateCallback');
var {
DOCUMENT_NODE,
ELEMENT_NODE,
DOCUMENT_FRAGMENT_NODE,
} = require('HTMLNodeType');
var ATTR_NAME = DOMProperty.ID_ATTRIBUTE_NAME;
var ROOT_ATTR_NAME = DOMProperty.ROOT_ATTRIBUTE_NAME;
var instancesByReactRootID = {};
/**
* Finds the index of the first character
* that's not common between the two given strings.
*
* @return {number} the index of the character where the strings diverge
*/
function firstDifferenceIndex(string1, string2) {
var minLen = Math.min(string1.length, string2.length);
for (var i = 0; i < minLen; i++) {
if (string1.charAt(i) !== string2.charAt(i)) {
return i;
}
}
return string1.length === string2.length ? -1 : minLen;
}
/**
* @param {DOMElement|DOMDocument} container DOM element that may contain
* a React component
* @return {?*} DOM element that may have the reactRoot ID, or null.
*/
function getReactRootElementInContainer(container) {
if (!container) {
return null;
}
if (container.nodeType === DOCUMENT_NODE) {
return container.documentElement;
} else {
return container.firstChild;
}
}
function internalGetID(node) {
// If node is something like a window, document, or text node, none of
// which support attributes or a .getAttribute method, gracefully return
// the empty string, as if the attribute were missing.
return (node.getAttribute && node.getAttribute(ATTR_NAME)) || '';
}
/**
* Mounts this component and inserts it into the DOM.
*
* @param {ReactComponent} wrapperInstance The instance to mount.
* @param {DOMElement} container DOM element to mount into.
* @param {ReactReconcileTransaction} transaction
* @param {boolean} shouldReuseMarkup If true, do not insert markup
*/
function mountComponentIntoNode(
wrapperInstance,
container,
transaction,
shouldReuseMarkup,
context,
) {
var markup = ReactReconciler.mountComponent(
wrapperInstance,
transaction,
null,
ReactDOMContainerInfo(wrapperInstance, container),
context,
0 /* parentDebugID */,
);
wrapperInstance._renderedComponent._topLevelWrapper = wrapperInstance;
ReactMount._mountImageIntoNode(
markup,
container,
wrapperInstance,
shouldReuseMarkup,
transaction,
);
}
/**
* Batched mount.
*
* @param {ReactComponent} componentInstance The instance to mount.
* @param {DOMElement} container DOM element to mount into.
* @param {boolean} shouldReuseMarkup If true, do not insert markup
*/
function batchedMountComponentIntoNode(
componentInstance,
container,
shouldReuseMarkup,
context,
) {
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(
/* useCreateElement */
!shouldReuseMarkup,
);
transaction.perform(
mountComponentIntoNode,
null,
componentInstance,
container,
transaction,
shouldReuseMarkup,
context,
);
ReactUpdates.ReactReconcileTransaction.release(transaction);
}
/**
* Unmounts a component and removes it from the DOM.
*
* @param {ReactComponent} instance React component instance.
* @param {DOMElement} container DOM element to unmount from.
* @final
* @internal
* @see {ReactMount.unmountComponentAtNode}
*/
function unmountComponentFromNode(instance, container) {
if (__DEV__) {
ReactInstrumentation.debugTool.onBeginFlush();
}
ReactReconciler.unmountComponent(
instance,
false /* safely */,
false /* skipLifecycle */,
);
if (__DEV__) {
ReactInstrumentation.debugTool.onEndFlush();
}
if (container.nodeType === DOCUMENT_NODE) {
container = container.documentElement;
}
// http://jsperf.com/emptying-a-node
while (container.lastChild) {
container.removeChild(container.lastChild);
}
}
/**
* True if the supplied DOM node has a direct React-rendered child that is
* not a React root element. Useful for warning in `render`,
* `unmountComponentAtNode`, etc.
*
* @param {?DOMElement} node The candidate DOM node.
* @return {boolean} True if the DOM element contains a direct child that was
* rendered by React but is not a root element.
* @internal
*/
function hasNonRootReactChild(container) {
var rootEl = getReactRootElementInContainer(container);
if (rootEl) {
var inst = ReactDOMComponentTree.getInstanceFromNode(rootEl);
return !!(inst && inst._hostParent);
}
}
/**
* True if the supplied DOM node is a React DOM element and
* it has been rendered by another copy of React.
*
* @param {?DOMElement} node The candidate DOM node.
* @return {boolean} True if the DOM has been rendered by another copy of React
* @internal
*/
function nodeIsRenderedByOtherInstance(container) {
var rootEl = getReactRootElementInContainer(container);
return !!(rootEl &&
isReactNode(rootEl) &&
!ReactDOMComponentTree.getInstanceFromNode(rootEl));
}
/**
* True if the supplied DOM node is a valid node element.
*
* @param {?DOMElement} node The candidate DOM node.
* @return {boolean} True if the DOM is a valid DOM node.
* @internal
*/
function isValidContainer(node) {
return !!(node &&
(node.nodeType === ELEMENT_NODE ||
node.nodeType === DOCUMENT_NODE ||
node.nodeType === DOCUMENT_FRAGMENT_NODE));
}
/**
* True if the supplied DOM node is a valid React node element.
*
* @param {?DOMElement} node The candidate DOM node.
* @return {boolean} True if the DOM is a valid React DOM node.
* @internal
*/
function isReactNode(node) {
return (
isValidContainer(node) &&
(node.hasAttribute(ROOT_ATTR_NAME) || node.hasAttribute(ATTR_NAME))
);
}
function getHostRootInstanceInContainer(container) {
var rootEl = getReactRootElementInContainer(container);
var prevHostInstance =
rootEl && ReactDOMComponentTree.getInstanceFromNode(rootEl);
return prevHostInstance && !prevHostInstance._hostParent
? prevHostInstance
: null;
}
function getTopLevelWrapperInContainer(container) {
var root = getHostRootInstanceInContainer(container);
return root ? root._hostContainerInfo._topLevelWrapper : null;
}
/**
* Temporary (?) hack so that we can store all top-level pending updates on
* composites instead of having to worry about different types of components
* here.
*/
var topLevelRootCounter = 1;
var TopLevelWrapper = function() {
this.rootID = topLevelRootCounter++;
};
TopLevelWrapper.prototype.isReactComponent = {};
if (__DEV__) {
TopLevelWrapper.displayName = 'TopLevelWrapper';
}
TopLevelWrapper.prototype.render = function() {
return this.props.child;
};
TopLevelWrapper.isReactTopLevelWrapper = true;
/**
* Mounting is the process of initializing a React component by creating its
* representative DOM elements and inserting them into a supplied `container`.
* Any prior content inside `container` is destroyed in the process.
*
* ReactMount.render(
* component,
* document.getElementById('container')
* );
*
* <div id="container"> <-- Supplied `container`.
* <div data-reactid=".3"> <-- Rendered reactRoot of React
* // ... component.
* </div>
* </div>
*
* Inside of `container`, the first element rendered is the "reactRoot".
*/
var ReactMount = {
TopLevelWrapper: TopLevelWrapper,
/**
* Used by devtools. The keys are not important.
*/
_instancesByReactRootID: instancesByReactRootID,
/**
* This is a hook provided to support rendering React components while
* ensuring that the apparent scroll position of its `container` does not
* change.
*
* @param {DOMElement} container The `container` being rendered into.
* @param {function} renderCallback This must be called once to do the render.
*/
scrollMonitor: function(container, renderCallback) {
renderCallback();
},
/**
* Take a component that's already mounted into the DOM and replace its props
* @param {ReactComponent} prevComponent component instance already in the DOM
* @param {ReactElement} nextElement component instance to render
* @param {DOMElement} container container to render into
* @param {?function} callback function triggered on completion
*/
_updateRootComponent: function(
prevComponent,
nextElement,
nextContext,
container,
callback,
) {
ReactMount.scrollMonitor(container, function() {
ReactUpdateQueue.enqueueElementInternal(
prevComponent,
nextElement,
nextContext,
);
if (callback) {
ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback);
}
});
return prevComponent;
},
/**
* Render a new component into the DOM. Hooked by hooks!
*
* @param {ReactElement} nextElement element to render
* @param {DOMElement} container container to render into
* @param {boolean} shouldReuseMarkup if we should skip the markup insertion
* @return {ReactComponent} nextComponent
*/
_renderNewRootComponent: function(
nextElement,
container,
shouldReuseMarkup,
context,
callback,
) {
// Various parts of our code (such as ReactCompositeComponent's
// _renderValidatedComponent) assume that calls to render aren't nested;
// verify that that's the case.
warning(
ReactCurrentOwner.current == null,
'_renderNewRootComponent(): Render methods should be a pure function ' +
'of props and state; triggering nested component updates from ' +
'render is not allowed. If necessary, trigger nested updates in ' +
'componentDidUpdate.\n\nCheck the render method of %s.',
(ReactCurrentOwner.current && ReactCurrentOwner.current.getName()) ||
'ReactCompositeComponent',
);
invariant(
isValidContainer(container),
'_registerComponent(...): Target container is not a DOM element.',
);
var componentInstance = instantiateReactComponent(nextElement, false);
if (callback) {
componentInstance._pendingCallbacks = [
function() {
validateCallback(callback);
callback.call(
componentInstance._renderedComponent.getPublicInstance(),
);
},
];
}
// The initial render is synchronous but any updates that happen during
// rendering, in componentWillMount or componentDidMount, will be batched
// according to the current batching strategy.
ReactUpdates.batchedUpdates(
batchedMountComponentIntoNode,
componentInstance,
container,
shouldReuseMarkup,
context,
);
var wrapperID = componentInstance._instance.rootID;
instancesByReactRootID[wrapperID] = componentInstance;
return componentInstance;
},
/**
* Renders a React component into the DOM in the supplied `container`.
*
* If the React component was previously rendered into `container`, this will
* perform an update on it and only mutate the DOM as necessary to reflect the
* latest React component.
*
* @param {ReactComponent} parentComponent The conceptual parent of this render tree.
* @param {ReactElement} nextElement Component element to render.
* @param {DOMElement} container DOM element to render into.
* @param {?function} callback function triggered on completion
* @return {ReactComponent} Component instance rendered in `container`.
*/
renderSubtreeIntoContainer: function(
parentComponent,
nextElement,
container,
callback,
) {
invariant(
parentComponent != null && ReactInstanceMap.has(parentComponent),
'parentComponent must be a valid React Component',
);
return ReactMount._renderSubtreeIntoContainer(
parentComponent,
nextElement,
container,
callback,
);
},
_renderSubtreeIntoContainer: function(
parentComponent,
nextElement,
container,
callback,
) {
callback = callback === undefined ? null : callback;
if (__DEV__) {
warning(
callback === null || typeof callback === 'function',
'render(...): Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
'' + callback,
);
}
if (!React.isValidElement(nextElement)) {
if (typeof nextElement === 'string') {
invariant(
false,
'ReactDOM.render(): Invalid component element. Instead of ' +
"passing a string like 'div', pass " +
"React.createElement('div') or <div />.",
);
} else if (typeof nextElement === 'function') {
invariant(
false,
'ReactDOM.render(): Invalid component element. Instead of ' +
'passing a class like Foo, pass React.createElement(Foo) ' +
'or <Foo />.',
);
} else if (
nextElement != null &&
typeof nextElement.props !== 'undefined'
) {
// Check if it quacks like an element
invariant(
false,
'ReactDOM.render(): Invalid component element. This may be ' +
'caused by unintentionally loading two independent copies ' +
'of React.',
);
} else {
invariant(false, 'ReactDOM.render(): Invalid component element.');
}
}
warning(
!container ||
!container.tagName ||
container.tagName.toUpperCase() !== 'BODY',
'render(): Rendering components directly into document.body is ' +
'discouraged, since its children are often manipulated by third-party ' +
'scripts and browser extensions. This may lead to subtle ' +
'reconciliation issues. Try rendering into a container element created ' +
'for your app.',
);
var nextWrappedElement = React.createElement(TopLevelWrapper, {
child: nextElement,
});
var nextContext = getContextForSubtree(parentComponent);
var prevComponent = getTopLevelWrapperInContainer(container);
if (prevComponent) {
var prevWrappedElement = prevComponent._currentElement;
var prevElement = prevWrappedElement.props.child;
if (shouldUpdateReactComponent(prevElement, nextElement)) {
var publicInst = prevComponent._renderedComponent.getPublicInstance();
var updatedCallback =
callback &&
function() {
validateCallback(callback);
callback.call(publicInst);
};
ReactMount._updateRootComponent(
prevComponent,
nextWrappedElement,
nextContext,
container,
updatedCallback,
);
return publicInst;
} else {
ReactMount.unmountComponentAtNode(container);
}
}
var reactRootElement = getReactRootElementInContainer(container);
var containerHasReactMarkup =
reactRootElement && !!internalGetID(reactRootElement);
var containerHasNonRootReactChild = hasNonRootReactChild(container);
if (__DEV__) {
warning(
!containerHasNonRootReactChild,
'render(...): Replacing React-rendered children with a new root ' +
'component. If you intended to update the children of this node, ' +
'you should instead have the existing children update their state ' +
'and render the new components instead of calling ReactDOM.render.',
);
if (!containerHasReactMarkup || reactRootElement.nextSibling) {
var rootElementSibling = reactRootElement;
while (rootElementSibling) {
if (internalGetID(rootElementSibling)) {
warning(
false,
'render(): Target node has markup rendered by React, but there ' +
'are unrelated nodes as well. This is most commonly caused by ' +
'white-space inserted around server-rendered markup.',
);
break;
}
rootElementSibling = rootElementSibling.nextSibling;
}
}
}
var shouldReuseMarkup =
containerHasReactMarkup &&
!prevComponent &&
!containerHasNonRootReactChild;
var component = ReactMount._renderNewRootComponent(
nextWrappedElement,
container,
shouldReuseMarkup,
nextContext,
callback,
)._renderedComponent.getPublicInstance();
return component;
},
/**
* Renders a React component into the DOM in the supplied `container`.
* See https://facebook.github.io/react/docs/react-dom.html#render
*
* If the React component was previously rendered into `container`, this will
* perform an update on it and only mutate the DOM as necessary to reflect the
* latest React component.
*
* @param {ReactElement} nextElement Component element to render.
* @param {DOMElement} container DOM element to render into.
* @param {?function} callback function triggered on completion
* @return {ReactComponent} Component instance rendered in `container`.
*/
render: function(nextElement, container, callback) {
return ReactMount._renderSubtreeIntoContainer(
null,
nextElement,
container,
callback,
);
},
/**
* Unmounts and destroys the React component rendered in the `container`.
* See https://facebook.github.io/react/docs/react-dom.html#unmountcomponentatnode
*
* @param {DOMElement} container DOM element containing a React component.
* @return {boolean} True if a component was found in and unmounted from
* `container`
*/
unmountComponentAtNode: function(container) {
// Various parts of our code (such as ReactCompositeComponent's
// _renderValidatedComponent) assume that calls to render aren't nested;
// verify that that's the case. (Strictly speaking, unmounting won't cause a
// render but we still don't expect to be in a render call here.)
warning(
ReactCurrentOwner.current == null,
'unmountComponentAtNode(): Render methods should be a pure function ' +
'of props and state; triggering nested component updates from render ' +
'is not allowed. If necessary, trigger nested updates in ' +
'componentDidUpdate.\n\nCheck the render method of %s.',
(ReactCurrentOwner.current && ReactCurrentOwner.current.getName()) ||
'ReactCompositeComponent',
);
invariant(
isValidContainer(container),
'unmountComponentAtNode(...): Target container is not a DOM element.',
);
if (__DEV__) {
warning(
!nodeIsRenderedByOtherInstance(container),
"unmountComponentAtNode(): The node you're attempting to unmount " +
'was rendered by another copy of React.',
);
}
var prevComponent = getTopLevelWrapperInContainer(container);
if (!prevComponent) {
// Check if the node being unmounted was rendered by React, but isn't a
// root node.
var containerHasNonRootReactChild = hasNonRootReactChild(container);
// Check if the container itself is a React root node.
var isContainerReactRoot =
container.nodeType === ELEMENT_NODE &&
container.hasAttribute(ROOT_ATTR_NAME);
if (__DEV__) {
warning(
!containerHasNonRootReactChild,
"unmountComponentAtNode(): The node you're attempting to unmount " +
'was rendered by React and is not a top-level container. %s',
isContainerReactRoot
? 'You may have accidentally passed in a React root node instead ' +
'of its container.'
: 'Instead, have the parent component update its state and ' +
'rerender in order to remove this component.',
);
}
return false;
}
delete instancesByReactRootID[prevComponent._instance.rootID];
ReactUpdates.batchedUpdates(
unmountComponentFromNode,
prevComponent,
container,
);
return true;
},
_mountImageIntoNode: function(
markup,
container,
instance,
shouldReuseMarkup,
transaction,
) {
invariant(
isValidContainer(container),
'mountComponentIntoNode(...): Target container is not valid.',
);
if (shouldReuseMarkup) {
var rootElement = getReactRootElementInContainer(container);
if (ReactMarkupChecksum.canReuseMarkup(markup, rootElement)) {
ReactDOMComponentTree.precacheNode(instance, rootElement);
return;
} else {
var checksum = rootElement.getAttribute(
ReactMarkupChecksum.CHECKSUM_ATTR_NAME,
);
rootElement.removeAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME);
var rootMarkup = rootElement.outerHTML;
rootElement.setAttribute(
ReactMarkupChecksum.CHECKSUM_ATTR_NAME,
checksum,
);
var normalizedMarkup = markup;
if (__DEV__) {
// because rootMarkup is retrieved from the DOM, various normalizations
// will have occurred which will not be present in `markup`. Here,
// insert markup into a <div> or <iframe> depending on the container
// type to perform the same normalizations before comparing.
var normalizer;
if (container.nodeType === ELEMENT_NODE) {
normalizer = document.createElement('div');
normalizer.innerHTML = markup;
normalizedMarkup = normalizer.innerHTML;
} else {
normalizer = document.createElement('iframe');
document.body.appendChild(normalizer);
normalizer.contentDocument.write(markup);
normalizedMarkup =
normalizer.contentDocument.documentElement.outerHTML;
document.body.removeChild(normalizer);
}
}
var diffIndex = firstDifferenceIndex(normalizedMarkup, rootMarkup);
var difference =
' (client) ' +
normalizedMarkup.substring(diffIndex - 20, diffIndex + 20) +
'\n (server) ' +
rootMarkup.substring(diffIndex - 20, diffIndex + 20);
invariant(
container.nodeType !== DOCUMENT_NODE,
"You're trying to render a component to the document using " +
'server rendering but the checksum was invalid. This usually ' +
'means you rendered a different component type or props on ' +
'the client from the one on the server, or your render() ' +
'methods are impure. React cannot handle this case due to ' +
'cross-browser quirks by rendering at the document root. You ' +
'should look for environment dependent code in your components ' +
'and ensure the props are the same client and server side:\n%s',
difference,
);
if (__DEV__) {
warning(
false,
'React attempted to reuse markup in a container but the ' +
'checksum was invalid. This generally means that you are ' +
'using server rendering and the markup generated on the ' +
'server was not what the client was expecting. React injected ' +
'new markup to compensate which works but you have lost many ' +
'of the benefits of server rendering. Instead, figure out ' +
'why the markup being generated is different on the client ' +
'or server:\n%s',
difference,
);
}
}
}
invariant(
container.nodeType !== DOCUMENT_NODE,
"You're trying to render a component to the document but " +
"you didn't use server rendering. We can't do this " +
'without using server rendering due to cross-browser quirks. ' +
'See ReactDOMServer.renderToString() for server rendering.',
);
if (transaction.useCreateElement) {
while (container.lastChild) {
container.removeChild(container.lastChild);
}
DOMLazyTree.insertTreeBefore(container, markup, null);
} else {
setInnerHTML(container, markup);
ReactDOMComponentTree.precacheNode(instance, container.firstChild);
}
if (__DEV__) {
var hostNode = ReactDOMComponentTree.getInstanceFromNode(
container.firstChild,
);
if (hostNode._debugID !== 0) {
ReactInstrumentation.debugTool.onHostOperation({
instanceID: hostNode._debugID,
type: 'mount',
payload: markup.toString(),
});
}
}
},
};
module.exports = ReactMount;
@@ -1,178 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactReconcileTransaction
*/
'use strict';
var CallbackQueue = require('CallbackQueue');
var PooledClass = require('PooledClass');
var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
var ReactInputSelection = require('ReactInputSelection');
var ReactInstrumentation = require('ReactInstrumentation');
var Transaction = require('Transaction');
var ReactUpdateQueue = require('ReactUpdateQueue');
/**
* Ensures that, when possible, the selection range (currently selected text
* input) is not disturbed by performing the transaction.
*/
var SELECTION_RESTORATION = {
/**
* @return {Selection} Selection information.
*/
initialize: ReactInputSelection.getSelectionInformation,
/**
* @param {Selection} sel Selection information returned from `initialize`.
*/
close: ReactInputSelection.restoreSelection,
};
/**
* Suppresses events (blur/focus) that could be inadvertently dispatched due to
* high level DOM manipulations (like temporarily removing a text input from the
* DOM).
*/
var EVENT_SUPPRESSION = {
/**
* @return {boolean} The enabled status of `ReactBrowserEventEmitter` before
* the reconciliation.
*/
initialize: function() {
var currentlyEnabled = ReactBrowserEventEmitter.isEnabled();
ReactBrowserEventEmitter.setEnabled(false);
return currentlyEnabled;
},
/**
* @param {boolean} previouslyEnabled Enabled status of
* `ReactBrowserEventEmitter` before the reconciliation occurred. `close`
* restores the previous value.
*/
close: function(previouslyEnabled) {
ReactBrowserEventEmitter.setEnabled(previouslyEnabled);
},
};
/**
* Provides a queue for collecting `componentDidMount` and
* `componentDidUpdate` callbacks during the transaction.
*/
var ON_DOM_READY_QUEUEING = {
/**
* Initializes the internal `onDOMReady` queue.
*/
initialize: function() {
this.reactMountReady.reset();
},
/**
* After DOM is flushed, invoke all registered `onDOMReady` callbacks.
*/
close: function() {
this.reactMountReady.notifyAll();
},
};
/**
* Executed within the scope of the `Transaction` instance. Consider these as
* being member methods, but with an implied ordering while being isolated from
* each other.
*/
var TRANSACTION_WRAPPERS = [
SELECTION_RESTORATION,
EVENT_SUPPRESSION,
ON_DOM_READY_QUEUEING,
];
if (__DEV__) {
TRANSACTION_WRAPPERS.push({
initialize: ReactInstrumentation.debugTool.onBeginFlush,
close: ReactInstrumentation.debugTool.onEndFlush,
});
}
/**
* Currently:
* - The order that these are listed in the transaction is critical:
* - Suppresses events.
* - Restores selection range.
*
* Future:
* - Restore document/overflow scroll positions that were unintentionally
* modified via DOM insertions above the top viewport boundary.
* - Implement/integrate with customized constraint based layout system and keep
* track of which dimensions must be remeasured.
*
* @class ReactReconcileTransaction
*/
function ReactReconcileTransaction(useCreateElement) {
this.reinitializeTransaction();
// Only server-side rendering really needs this option (see
// `ReactServerRendering`), but server-side uses
// `ReactServerRenderingTransaction` instead. This option is here so that it's
// accessible and defaults to false when `ReactDOMComponent` and
// `ReactDOMTextComponent` checks it in `mountComponent`.`
this.renderToStaticMarkup = false;
this.reactMountReady = CallbackQueue.getPooled();
this.useCreateElement = useCreateElement;
}
var Mixin = {
/**
* @see Transaction
* @abstract
* @final
* @return {array<object>} List of operation wrap procedures.
* TODO: convert to array<TransactionWrapper>
*/
getTransactionWrappers: function() {
return TRANSACTION_WRAPPERS;
},
/**
* @return {object} The queue to collect `onDOMReady` callbacks with.
*/
getReactMountReady: function() {
return this.reactMountReady;
},
/**
* @return {object} The queue to collect React async events.
*/
getUpdateQueue: function() {
return ReactUpdateQueue;
},
/**
* Save current transaction state -- if the return value from this method is
* passed to `rollback`, the transaction will be reset to that state.
*/
checkpoint: function() {
// reactMountReady is the our only stateful wrapper
return this.reactMountReady.checkpoint();
},
rollback: function(checkpoint) {
this.reactMountReady.rollback(checkpoint);
},
/**
* `PooledClass` looks for this, and will invoke this before allowing this
* instance to be reused.
*/
destructor: function() {
CallbackQueue.release(this.reactMountReady);
this.reactMountReady = null;
},
};
Object.assign(ReactReconcileTransaction.prototype, Transaction, Mixin);
PooledClass.addPoolingTo(ReactReconcileTransaction);
module.exports = ReactReconcileTransaction;
@@ -1,337 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactDOMInput
*/
'use strict';
var DOMPropertyOperations = require('DOMPropertyOperations');
var ReactControlledValuePropTypes = require('ReactControlledValuePropTypes');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');
if (__DEV__) {
var {
getStackAddendumByID,
} = require('ReactGlobalSharedState').ReactComponentTreeHook;
}
var didWarnValueDefaultValue = false;
var didWarnCheckedDefaultChecked = false;
var didWarnControlledToUncontrolled = false;
var didWarnUncontrolledToControlled = false;
function isControlled(props) {
var usesChecked = props.type === 'checkbox' || props.type === 'radio';
return usesChecked ? props.checked != null : props.value != null;
}
/**
* Implements an <input> host component that allows setting these optional
* props: `checked`, `value`, `defaultChecked`, and `defaultValue`.
*
* If `checked` or `value` are not supplied (or null/undefined), user actions
* that affect the checked state or value will trigger updates to the element.
*
* If they are supplied (and not null/undefined), the rendered element will not
* trigger updates to the element. Instead, the props must change in order for
* the rendered element to be updated.
*
* The rendered element will be initialized as unchecked (or `defaultChecked`)
* with an empty value (or `defaultValue`).
*
* @see http://www.w3.org/TR/2012/WD-html5-20121025/the-input-element.html
*/
var ReactDOMInput = {
getHostProps: function(inst, props) {
var value = props.value;
var checked = props.checked;
var hostProps = Object.assign(
{
// Make sure we set .type before any other properties (setting .value
// before .type means .value is lost in IE11 and below)
type: undefined,
// Make sure we set .step before .value (setting .value before .step
// means .value is rounded on mount, based upon step precision)
step: undefined,
// Make sure we set .min & .max before .value (to ensure proper order
// in corner cases such as min or max deriving from value, e.g. Issue #7170)
min: undefined,
max: undefined,
},
props,
{
defaultChecked: undefined,
defaultValue: undefined,
value: value != null ? value : inst._wrapperState.initialValue,
checked: checked != null ? checked : inst._wrapperState.initialChecked,
},
);
return hostProps;
},
mountWrapper: function(inst, props) {
if (__DEV__) {
var owner = inst._currentElement._owner;
ReactControlledValuePropTypes.checkPropTypes('input', props, () =>
getStackAddendumByID(inst._debugID),
);
if (
props.checked !== undefined &&
props.defaultChecked !== undefined &&
!didWarnCheckedDefaultChecked
) {
warning(
false,
'%s contains an input of type %s with both checked and defaultChecked props. ' +
'Input elements must be either controlled or uncontrolled ' +
'(specify either the checked prop, or the defaultChecked prop, but not ' +
'both). Decide between using a controlled or uncontrolled input ' +
'element and remove one of these props. More info: ' +
'https://fb.me/react-controlled-components',
(owner && owner.getName()) || 'A component',
props.type,
);
didWarnCheckedDefaultChecked = true;
}
if (
props.value !== undefined &&
props.defaultValue !== undefined &&
!didWarnValueDefaultValue
) {
warning(
false,
'%s contains an input of type %s with both value and defaultValue props. ' +
'Input elements must be either controlled or uncontrolled ' +
'(specify either the value prop, or the defaultValue prop, but not ' +
'both). Decide between using a controlled or uncontrolled input ' +
'element and remove one of these props. More info: ' +
'https://fb.me/react-controlled-components',
(owner && owner.getName()) || 'A component',
props.type,
);
didWarnValueDefaultValue = true;
}
}
var defaultValue = props.defaultValue;
inst._wrapperState = {
initialChecked: props.checked != null
? props.checked
: props.defaultChecked,
initialValue: props.value != null ? props.value : defaultValue,
listeners: null,
controlled: isControlled(props),
};
},
updateWrapper: function(inst) {
var props = inst._currentElement.props;
if (__DEV__) {
var controlled = isControlled(props);
if (
!inst._wrapperState.controlled &&
controlled &&
!didWarnUncontrolledToControlled
) {
warning(
false,
'A component is changing an uncontrolled input of type %s to be controlled. ' +
'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
'Decide between using a controlled or uncontrolled input ' +
'element for the lifetime of the component. More info: https://fb.me/react-controlled-components%s',
props.type,
getStackAddendumByID(inst._debugID),
);
didWarnUncontrolledToControlled = true;
}
if (
inst._wrapperState.controlled &&
!controlled &&
!didWarnControlledToUncontrolled
) {
warning(
false,
'A component is changing a controlled input of type %s to be uncontrolled. ' +
'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
'Decide between using a controlled or uncontrolled input ' +
'element for the lifetime of the component. More info: https://fb.me/react-controlled-components%s',
props.type,
getStackAddendumByID(inst._debugID),
);
didWarnControlledToUncontrolled = true;
}
}
var checked = props.checked;
if (checked != null) {
DOMPropertyOperations.setValueForProperty(
ReactDOMComponentTree.getNodeFromInstance(inst),
'checked',
checked || false,
);
}
var node = ReactDOMComponentTree.getNodeFromInstance(inst);
var value = props.value;
if (value != null) {
if (value === 0 && node.value === '') {
node.value = '0';
// Note: IE9 reports a number inputs as 'text', so check props instead.
} else if (props.type === 'number') {
// Simulate `input.valueAsNumber`. IE9 does not support it
var valueAsNumber = parseFloat(node.value, 10) || 0;
if (
// eslint-disable-next-line
value != valueAsNumber ||
// eslint-disable-next-line
(value == valueAsNumber && node.value != value)
) {
// Cast `value` to a string to ensure the value is set correctly. While
// browsers typically do this as necessary, jsdom doesn't.
node.value = '' + value;
}
} else if (node.value !== '' + value) {
// Cast `value` to a string to ensure the value is set correctly. While
// browsers typically do this as necessary, jsdom doesn't.
node.value = '' + value;
}
} else {
if (props.value == null && props.defaultValue != null) {
// In Chrome, assigning defaultValue to certain input types triggers input validation.
// For number inputs, the display value loses trailing decimal points. For email inputs,
// Chrome raises "The specified value <x> is not a valid email address".
//
// Here we check to see if the defaultValue has actually changed, avoiding these problems
// when the user is inputting text
//
// https://github.com/facebook/react/issues/7253
if (node.defaultValue !== '' + props.defaultValue) {
node.defaultValue = '' + props.defaultValue;
}
}
if (props.checked == null && props.defaultChecked != null) {
node.defaultChecked = !!props.defaultChecked;
}
}
},
postMountWrapper: function(inst) {
var props = inst._currentElement.props;
// This is in postMount because we need access to the DOM node, which is not
// available until after the component has mounted.
var node = ReactDOMComponentTree.getNodeFromInstance(inst);
// Detach value from defaultValue. We won't do anything if we're working on
// submit or reset inputs as those values & defaultValues are linked. They
// are not resetable nodes so this operation doesn't matter and actually
// removes browser-default values (eg "Submit Query") when no value is
// provided.
switch (props.type) {
case 'submit':
case 'reset':
break;
case 'color':
case 'date':
case 'datetime':
case 'datetime-local':
case 'month':
case 'time':
case 'week':
// This fixes the no-show issue on iOS Safari and Android Chrome:
// https://github.com/facebook/react/issues/7233
node.value = '';
node.value = node.defaultValue;
break;
default:
node.value = node.value;
break;
}
// Normally, we'd just do `node.checked = node.checked` upon initial mount, less this bug
// this is needed to work around a chrome bug where setting defaultChecked
// will sometimes influence the value of checked (even after detachment).
// Reference: https://bugs.chromium.org/p/chromium/issues/detail?id=608416
// We need to temporarily unset name to avoid disrupting radio button groups.
var name = node.name;
if (name !== '') {
node.name = '';
}
node.defaultChecked = !node.defaultChecked;
node.defaultChecked = !node.defaultChecked;
if (name !== '') {
node.name = name;
}
},
restoreControlledState: function(inst) {
if (inst._rootNodeID) {
// DOM component is still mounted; update
ReactDOMInput.updateWrapper(inst);
}
var props = inst._currentElement.props;
updateNamedCousins(inst, props);
},
};
function updateNamedCousins(thisInstance, props) {
var name = props.name;
if (props.type === 'radio' && name != null) {
var rootNode = ReactDOMComponentTree.getNodeFromInstance(thisInstance);
var queryRoot = rootNode;
while (queryRoot.parentNode) {
queryRoot = queryRoot.parentNode;
}
// If `rootNode.form` was non-null, then we could try `form.elements`,
// but that sometimes behaves strangely in IE8. We could also try using
// `form.getElementsByName`, but that will only return direct children
// and won't include inputs that use the HTML5 `form=` attribute. Since
// the input might not even be in a form. It might not even be in the
// document. Let's just use the local `querySelectorAll` to ensure we don't
// miss anything.
var group = queryRoot.querySelectorAll(
'input[name=' + JSON.stringify('' + name) + '][type="radio"]',
);
for (var i = 0; i < group.length; i++) {
var otherNode = group[i];
if (otherNode === rootNode || otherNode.form !== rootNode.form) {
continue;
}
// This will throw if radio buttons rendered by different copies of React
// and the same name are rendered into the same form (same as #1939).
// That's probably okay; we don't support it just as we don't support
// mixing React radio buttons with non-React ones.
var otherInstance = ReactDOMComponentTree.getInstanceFromNode(otherNode);
invariant(
otherInstance,
'ReactDOMInput: Mixing React and non-React radio inputs with the ' +
'same `name` is not supported.',
);
// If this is a controlled radio button group, forcing the input that
// was previously checked to update will cause it to be come re-checked
// as appropriate.
if (otherInstance._rootNodeID) {
ReactDOMInput.updateWrapper(otherInstance);
}
}
}
}
module.exports = ReactDOMInput;
@@ -1,128 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactDOMOption
*/
'use strict';
var React = require('react');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactDOMSelect = require('ReactDOMSelect');
var warning = require('fbjs/lib/warning');
var didWarnInvalidOptionChildren = false;
function flattenChildren(children) {
var content = '';
// Flatten children and warn if they aren't strings or numbers;
// invalid types are ignored.
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.',
);
}
});
return content;
}
/**
* Implements an <option> host component that warns when `selected` is set.
*/
var ReactDOMOption = {
mountWrapper: function(inst, props, hostParent) {
// TODO (yungsters): Remove support for `selected` in <option>.
if (__DEV__) {
warning(
props.selected == null,
'Use the `defaultValue` or `value` props on <select> instead of ' +
'setting `selected` on <option>.',
);
}
// Look up whether this option is 'selected'
var selectValue = null;
if (hostParent != null) {
var selectParent = hostParent;
if (selectParent._tag === 'optgroup') {
selectParent = selectParent._hostParent;
}
if (selectParent != null && selectParent._tag === 'select') {
selectValue = ReactDOMSelect.getSelectValueContext(selectParent);
}
}
// If the value is null (e.g., no specified value or after initial mount)
// or missing (e.g., for <datalist>), we don't change props.selected
var selected = null;
if (selectValue != null) {
var value;
if (props.value != null) {
value = props.value + '';
} else {
value = flattenChildren(props.children);
}
selected = false;
if (Array.isArray(selectValue)) {
// multiple
for (var i = 0; i < selectValue.length; i++) {
if ('' + selectValue[i] === value) {
selected = true;
break;
}
}
} else {
selected = '' + selectValue === value;
}
}
inst._wrapperState = {selected: selected};
},
postMountWrapper: function(inst) {
// value="" should make a value attribute (#6219)
var props = inst._currentElement.props;
if (props.value != null) {
var node = ReactDOMComponentTree.getNodeFromInstance(inst);
node.setAttribute('value', props.value);
}
},
getHostProps: function(inst, props) {
var hostProps = Object.assign(
{selected: undefined, children: undefined},
props,
);
// Read state only from initial mount because <select> updates value
// manually; we need the initial state only for server rendering
if (inst._wrapperState.selected != null) {
hostProps.selected = inst._wrapperState.selected;
}
var content = flattenChildren(props.children);
if (content) {
hostProps.children = content;
}
return hostProps;
},
};
module.exports = ReactDOMOption;
@@ -1,203 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactDOMSelect
*/
'use strict';
var ReactControlledValuePropTypes = require('ReactControlledValuePropTypes');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var warning = require('fbjs/lib/warning');
if (__DEV__) {
var {
getStackAddendumByID,
} = require('ReactGlobalSharedState').ReactComponentTreeHook;
}
var didWarnValueDefaultValue = false;
function getDeclarationErrorAddendum(owner) {
if (owner) {
var name = owner.getName();
if (name) {
return '\n\nCheck the render method of `' + name + '`.';
}
}
return '';
}
var valuePropNames = ['value', 'defaultValue'];
/**
* Validation function for `value` and `defaultValue`.
* @private
*/
function checkSelectPropTypes(inst, props) {
var owner = inst._currentElement._owner;
ReactControlledValuePropTypes.checkPropTypes('select', props, () =>
getStackAddendumByID(inst._debugID),
);
for (var i = 0; i < valuePropNames.length; i++) {
var propName = valuePropNames[i];
if (props[propName] == null) {
continue;
}
var isArray = Array.isArray(props[propName]);
if (props.multiple && !isArray) {
warning(
false,
'The `%s` prop supplied to <select> must be an array if ' +
'`multiple` is true.%s',
propName,
getDeclarationErrorAddendum(owner),
);
} else if (!props.multiple && isArray) {
warning(
false,
'The `%s` prop supplied to <select> must be a scalar ' +
'value if `multiple` is false.%s',
propName,
getDeclarationErrorAddendum(owner),
);
}
}
}
/**
* @param {ReactDOMComponent} inst
* @param {boolean} multiple
* @param {*} propValue A stringable (with `multiple`, a list of stringables).
* @private
*/
function updateOptions(inst, multiple, propValue) {
var options = ReactDOMComponentTree.getNodeFromInstance(inst).options;
if (multiple) {
let selectedValue = {};
for (let i = 0; i < propValue.length; i++) {
// Prefix to avoid chaos with special keys.
selectedValue['$' + propValue[i]] = true;
}
for (let i = 0; i < options.length; i++) {
var selected = selectedValue.hasOwnProperty('$' + options[i].value);
if (options[i].selected !== selected) {
options[i].selected = selected;
}
}
} else {
// Do not set `select.value` as exact behavior isn't consistent across all
// browsers for all cases.
let selectedValue = '' + propValue;
for (let i = 0; i < options.length; i++) {
if (options[i].value === selectedValue) {
options[i].selected = true;
return;
}
}
if (options.length) {
options[0].selected = true;
}
}
}
/**
* Implements a <select> host component that allows optionally setting the
* props `value` and `defaultValue`. If `multiple` is false, the prop must be a
* stringable. If `multiple` is true, the prop must be an array of stringables.
*
* If `value` is not supplied (or null/undefined), user actions that change the
* selected option will trigger updates to the rendered options.
*
* If it is supplied (and not null/undefined), the rendered options will not
* update in response to user actions. Instead, the `value` prop must change in
* order for the rendered options to update.
*
* If `defaultValue` is provided, any options with the supplied values will be
* selected.
*/
var ReactDOMSelect = {
getHostProps: function(inst, props) {
return Object.assign({}, props, {
value: undefined,
});
},
mountWrapper: function(inst, props) {
if (__DEV__) {
checkSelectPropTypes(inst, props);
}
var value = props.value;
inst._wrapperState = {
initialValue: value != null ? value : props.defaultValue,
listeners: null,
wasMultiple: !!props.multiple,
};
if (
props.value !== undefined &&
props.defaultValue !== undefined &&
!didWarnValueDefaultValue
) {
warning(
false,
'Select elements must be either controlled or uncontrolled ' +
'(specify either the value prop, or the defaultValue prop, but not ' +
'both). Decide between using a controlled or uncontrolled select ' +
'element and remove one of these props. More info: ' +
'https://fb.me/react-controlled-components',
);
didWarnValueDefaultValue = true;
}
},
getSelectValueContext: function(inst) {
// ReactDOMOption looks at this initial value so the initial generated
// markup has correct `selected` attributes
return inst._wrapperState.initialValue;
},
postUpdateWrapper: function(inst) {
var props = inst._currentElement.props;
// After the initial mount, we control selected-ness manually so don't pass
// this value down
inst._wrapperState.initialValue = undefined;
var wasMultiple = inst._wrapperState.wasMultiple;
inst._wrapperState.wasMultiple = !!props.multiple;
var value = props.value;
if (value != null) {
updateOptions(inst, !!props.multiple, value);
} else if (wasMultiple !== !!props.multiple) {
// For simplicity, reapply `defaultValue` if `multiple` is toggled.
if (props.defaultValue != null) {
updateOptions(inst, !!props.multiple, props.defaultValue);
} else {
// Revert the select back to its default unselected state.
updateOptions(inst, !!props.multiple, props.multiple ? [] : '');
}
}
},
restoreControlledState: function(inst) {
if (inst._rootNodeID) {
var props = inst._currentElement.props;
var value = props.value;
if (value != null) {
updateOptions(inst, !!props.multiple, value);
}
}
},
};
module.exports = ReactDOMSelect;
@@ -1,173 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactDOMTextarea
*/
'use strict';
var ReactControlledValuePropTypes = require('ReactControlledValuePropTypes');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');
if (__DEV__) {
var {
getStackAddendumByID,
} = require('ReactGlobalSharedState').ReactComponentTreeHook;
}
var didWarnValDefaultVal = false;
/**
* Implements a <textarea> host component that allows setting `value`, and
* `defaultValue`. This differs from the traditional DOM API because value is
* usually set as PCDATA children.
*
* If `value` is not supplied (or null/undefined), user actions that affect the
* value will trigger updates to the element.
*
* If `value` is supplied (and not null/undefined), the rendered element will
* not trigger updates to the element. Instead, the `value` prop must change in
* order for the rendered element to be updated.
*
* The rendered element will be initialized with an empty value, the prop
* `defaultValue` if specified, or the children content (deprecated).
*/
var ReactDOMTextarea = {
getHostProps: function(inst, props) {
invariant(
props.dangerouslySetInnerHTML == null,
'`dangerouslySetInnerHTML` does not make sense on <textarea>.',
);
// Always set children to the same thing. In IE9, the selection range will
// get reset if `textContent` is mutated. We could add a check in setTextContent
// to only set the value if/when the value differs from the node value (which would
// completely solve this IE9 bug), but Sebastian+Sophie seemed to like this
// solution. The value can be a boolean or object so that's why it's forced
// to be a string.
var hostProps = Object.assign({}, props, {
value: undefined,
defaultValue: undefined,
children: '' + inst._wrapperState.initialValue,
});
return hostProps;
},
mountWrapper: function(inst, props) {
if (__DEV__) {
ReactControlledValuePropTypes.checkPropTypes('textarea', props, () =>
getStackAddendumByID(inst._debugID),
);
if (
props.value !== undefined &&
props.defaultValue !== undefined &&
!didWarnValDefaultVal
) {
warning(
false,
'Textarea elements must be either controlled or uncontrolled ' +
'(specify either the value prop, or the defaultValue prop, but not ' +
'both). Decide between using a controlled or uncontrolled textarea ' +
'and remove one of these props. More info: ' +
'https://fb.me/react-controlled-components',
);
didWarnValDefaultVal = true;
}
}
var value = props.value;
var initialValue = value;
// Only bother fetching default value if we're going to use it
if (value == null) {
var defaultValue = props.defaultValue;
// TODO (yungsters): Remove support for children content in <textarea>.
var children = props.children;
if (children != null) {
if (__DEV__) {
warning(
false,
'Use the `defaultValue` or `value` props instead of setting ' +
'children on <textarea>.',
);
}
invariant(
defaultValue == null,
'If you supply `defaultValue` on a <textarea>, do not pass children.',
);
if (Array.isArray(children)) {
invariant(
children.length <= 1,
'<textarea> can only have at most one child.',
);
children = children[0];
}
defaultValue = '' + children;
}
if (defaultValue == null) {
defaultValue = '';
}
initialValue = defaultValue;
}
inst._wrapperState = {
initialValue: '' + initialValue,
listeners: null,
};
},
updateWrapper: function(inst) {
var props = inst._currentElement.props;
var node = ReactDOMComponentTree.getNodeFromInstance(inst);
var value = props.value;
if (value != null) {
// Cast `value` to a string to ensure the value is set correctly. While
// browsers typically do this as necessary, jsdom doesn't.
var newValue = '' + value;
// To avoid side effects (such as losing text selection), only set value if changed
if (newValue !== node.value) {
node.value = newValue;
}
if (props.defaultValue == null) {
node.defaultValue = newValue;
}
}
if (props.defaultValue != null) {
node.defaultValue = props.defaultValue;
}
},
postMountWrapper: function(inst) {
// This is in postMount because we need access to the DOM node, which is not
// available until after the component has mounted.
var node = ReactDOMComponentTree.getNodeFromInstance(inst);
var textContent = node.textContent;
// Only set node.value if textContent is equal to the expected
// initial value. In IE10/IE11 there is a bug where the placeholder attribute
// will populate textContent as well.
// https://developer.microsoft.com/microsoft-edge/platform/issues/101525/
if (textContent === inst._wrapperState.initialValue) {
node.value = textContent;
}
},
restoreControlledState: function(inst) {
if (inst._rootNodeID) {
// DOM component is still mounted; update
ReactDOMTextarea.updateWrapper(inst);
}
},
};
module.exports = ReactDOMTextarea;
@@ -1,53 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactMarkupChecksum
*/
'use strict';
var adler32 = require('adler32');
var TAG_END = /\/?>/;
var COMMENT_START = /^<\!\-\-/;
var ReactMarkupChecksum = {
CHECKSUM_ATTR_NAME: 'data-react-checksum',
/**
* @param {string} markup Markup string
* @return {string} Markup string with checksum attribute attached
*/
addChecksumToMarkup: function(markup) {
var checksum = adler32(markup);
// Add checksum (handle both parent tags, comments and self-closing tags)
if (COMMENT_START.test(markup)) {
return markup;
} else {
return markup.replace(
TAG_END,
' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="' + checksum + '"$&',
);
}
},
/**
* @param {string} markup to use
* @param {DOMElement} element root React element
* @returns {boolean} whether or not the markup is the same
*/
canReuseMarkup: function(markup, element) {
var existingChecksum = element.getAttribute(
ReactMarkupChecksum.CHECKSUM_ATTR_NAME,
);
existingChecksum = existingChecksum && parseInt(existingChecksum, 10);
var markupChecksum = adler32(markup);
return markupChecksum === existingChecksum;
},
};
module.exports = ReactMarkupChecksum;
@@ -1,20 +0,0 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactServerBatchingStrategy
*/
'use strict';
var ReactServerBatchingStrategy = {
isBatchingUpdates: false,
batchedUpdates: function(callback) {
// Don't do anything here. During the server rendering we don't want to
// schedule any updates. We will simply ignore them.
},
};
module.exports = ReactServerBatchingStrategy;
@@ -1,102 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactServerRendering
*/
'use strict';
var React = require('react');
var ReactDOMContainerInfo = require('ReactDOMContainerInfo');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactMarkupChecksum = require('ReactMarkupChecksum');
var ReactReconciler = require('ReactReconciler');
var ReactServerBatchingStrategy = require('ReactServerBatchingStrategy');
var ReactServerRenderingTransaction = require('ReactServerRenderingTransaction');
var ReactUpdates = require('ReactUpdates');
var emptyObject = require('fbjs/lib/emptyObject');
var instantiateReactComponent = require('instantiateReactComponent');
var invariant = require('fbjs/lib/invariant');
var pendingTransactions = 0;
/**
* @param {ReactElement} element
* @param {boolean} makeStaticMarkup
* @return {string} the HTML markup
*/
function renderToStringImpl(element, makeStaticMarkup) {
var transaction;
var previousBatchingStrategy;
try {
previousBatchingStrategy = ReactUpdates.injection.getBatchingStrategy();
ReactUpdates.injection.injectBatchingStrategy(ReactServerBatchingStrategy);
transaction = ReactServerRenderingTransaction.getPooled(makeStaticMarkup);
pendingTransactions++;
return transaction.perform(function() {
var componentInstance = instantiateReactComponent(element, true);
var markup = ReactReconciler.mountComponent(
componentInstance,
transaction,
null,
ReactDOMContainerInfo(),
emptyObject,
0 /* parentDebugID */,
);
if (__DEV__) {
ReactInstrumentation.debugTool.onUnmountComponent(
componentInstance._debugID,
);
}
if (!makeStaticMarkup) {
markup = ReactMarkupChecksum.addChecksumToMarkup(markup);
}
return markup;
}, null);
} finally {
pendingTransactions--;
ReactServerRenderingTransaction.release(transaction);
// Revert to the DOM batching strategy since these two renderers
// currently share these stateful modules.
if (!pendingTransactions) {
ReactUpdates.injection.injectBatchingStrategy(previousBatchingStrategy);
}
}
}
/**
* Render a ReactElement to its initial HTML. This should only be used on the
* server.
* See https://facebook.github.io/react/docs/react-dom-server.html#rendertostring
*/
function renderToString(element) {
invariant(
React.isValidElement(element),
'renderToString(): Invalid component element.',
);
return renderToStringImpl(element, false);
}
/**
* Similar to renderToString, except this doesn't create extra DOM attributes
* such as data-react-id that React uses internally.
* See https://facebook.github.io/react/docs/react-dom-server.html#rendertostaticmarkup
*/
function renderToStaticMarkup(element) {
invariant(
React.isValidElement(element),
'renderToStaticMarkup(): Invalid component element.',
);
return renderToStringImpl(element, true);
}
module.exports = {
renderToString: renderToString,
renderToStaticMarkup: renderToStaticMarkup,
};
@@ -1,86 +0,0 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactServerRenderingTransaction
*/
'use strict';
var PooledClass = require('PooledClass');
var Transaction = require('Transaction');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactServerUpdateQueue = require('ReactServerUpdateQueue');
/**
* Executed within the scope of the `Transaction` instance. Consider these as
* being member methods, but with an implied ordering while being isolated from
* each other.
*/
var TRANSACTION_WRAPPERS = [];
if (__DEV__) {
TRANSACTION_WRAPPERS.push({
initialize: ReactInstrumentation.debugTool.onBeginFlush,
close: ReactInstrumentation.debugTool.onEndFlush,
});
}
var noopCallbackQueue = {
enqueue: function() {},
};
/**
* @class ReactServerRenderingTransaction
* @param {boolean} renderToStaticMarkup
*/
function ReactServerRenderingTransaction(renderToStaticMarkup) {
this.reinitializeTransaction();
this.renderToStaticMarkup = renderToStaticMarkup;
this.useCreateElement = false;
this.updateQueue = new ReactServerUpdateQueue(this);
}
var Mixin = {
/**
* @see Transaction
* @abstract
* @final
* @return {array} Empty list of operation wrap procedures.
*/
getTransactionWrappers: function() {
return TRANSACTION_WRAPPERS;
},
/**
* @return {object} The queue to collect `onDOMReady` callbacks with.
*/
getReactMountReady: function() {
return noopCallbackQueue;
},
/**
* @return {object} The queue to collect React async events.
*/
getUpdateQueue: function() {
return this.updateQueue;
},
/**
* `PooledClass` looks for this, and will invoke this before allowing this
* instance to be reused.
*/
destructor: function() {},
checkpoint: function() {},
rollback: function() {},
};
Object.assign(ReactServerRenderingTransaction.prototype, Transaction, Mixin);
PooledClass.addPoolingTo(ReactServerRenderingTransaction);
module.exports = ReactServerRenderingTransaction;
@@ -1,150 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactServerUpdateQueue
* @flow
*/
'use strict';
var ReactUpdateQueue = require('ReactUpdateQueue');
var warning = require('fbjs/lib/warning');
import type {Transaction} from 'Transaction';
function warnNoop(
publicInstance: React$Component<any, any>,
callerName: string,
) {
if (__DEV__) {
var constructor = publicInstance.constructor;
warning(
false,
'%s(...): Can only update a mounting component. ' +
'This usually means you called %s() outside componentWillMount() on the server. ' +
'This is a no-op.\n\nPlease check the code for the %s component.',
callerName,
callerName,
(constructor && (constructor.displayName || constructor.name)) ||
'ReactClass',
);
}
}
/**
* This is the update queue used for server rendering.
* It delegates to ReactUpdateQueue while server rendering is in progress and
* switches to ReactNoopUpdateQueue after the transaction has completed.
* @class ReactServerUpdateQueue
* @param {Transaction} transaction
*/
class ReactServerUpdateQueue {
transaction: Transaction;
constructor(transaction: Transaction) {
this.transaction = transaction;
}
/**
* Checks whether or not this composite component is mounted.
* @param {ReactClass} publicInstance The instance we want to test.
* @return {boolean} True if mounted, false otherwise.
* @protected
* @final
*/
isMounted(publicInstance: React$Component<any, any>): boolean {
return false;
}
/**
* Forces an update. This should only be invoked when it is known with
* certainty that we are **not** in a DOM transaction.
*
* You may want to call this when you know that some deeper aspect of the
* component's state has changed but `setState` was not called.
*
* This will not invoke `shouldComponentUpdate`, but it will invoke
* `componentWillUpdate` and `componentDidUpdate`.
*
* @param {ReactClass} publicInstance The instance that should rerender.
* @param {?function} callback Called after component is updated.
* @param {?string} callerName Name of the calling function in the public API.
* @internal
*/
enqueueForceUpdate(
publicInstance: React$Component<any, any>,
callback?: Function,
callerName?: string,
) {
if (this.transaction.isInTransaction()) {
ReactUpdateQueue.enqueueForceUpdate(publicInstance, callback, callerName);
} else {
warnNoop(publicInstance, 'forceUpdate');
}
}
/**
* Replaces all of the state. Always use this or `setState` to mutate state.
* You should treat `this.state` as immutable.
*
* There is no guarantee that `this.state` will be immediately updated, so
* accessing `this.state` after calling this method may return the old value.
*
* @param {ReactClass} publicInstance The instance that should rerender.
* @param {object|function} completeState Next state.
* @param {?function} callback Called after component is updated.
* @param {?string} Name of the calling function in the public API.
* @internal
*/
enqueueReplaceState(
publicInstance: React$Component<any, any>,
completeState: Object | Function,
callback?: Function,
callerName?: string,
) {
if (this.transaction.isInTransaction()) {
ReactUpdateQueue.enqueueReplaceState(
publicInstance,
completeState,
callback,
callerName,
);
} else {
warnNoop(publicInstance, 'replaceState');
}
}
/**
* Sets a subset of the state. This only exists because _pendingState is
* internal. This provides a merging strategy that is not available to deep
* properties which is confusing. TODO: Expose pendingState or don't use it
* during the merge.
*
* @param {ReactClass} publicInstance The instance that should rerender.
* @param {object|function} partialState Next partial state to be merged with state.
* @internal
*/
enqueueSetState(
publicInstance: React$Component<any, any>,
partialState: Object | Function,
callback?: Function,
callerName?: string,
) {
if (this.transaction.isInTransaction()) {
ReactUpdateQueue.enqueueSetState(
publicInstance,
partialState,
callback,
callerName,
);
} else {
warnNoop(publicInstance, 'setState');
}
}
}
module.exports = ReactServerUpdateQueue;
@@ -1,43 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule shouldUpdateReactComponent
*/
'use strict';
/**
* Given a `prevElement` and `nextElement`, determines if the existing
* instance should be updated as opposed to being destroyed or replaced by a new
* instance. Both arguments are elements. This ensures that this logic can
* operate on stateless trees without any backing instance.
*
* @param {?object} prevElement
* @param {?object} nextElement
* @return {boolean} True if the existing instance should be updated.
* @protected
*/
function shouldUpdateReactComponent(prevElement, nextElement) {
var prevEmpty = prevElement === null || prevElement === false;
var nextEmpty = nextElement === null || nextElement === false;
if (prevEmpty || nextEmpty) {
return prevEmpty === nextEmpty;
}
var prevType = typeof prevElement;
var nextType = typeof nextElement;
if (prevType === 'string' || prevType === 'number') {
return nextType === 'string' || nextType === 'number';
} else {
return (
nextType === 'object' &&
prevElement.type === nextElement.type &&
prevElement.key === nextElement.key
);
}
}
module.exports = shouldUpdateReactComponent;
@@ -1,106 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule CallbackQueue
* @flow
*/
'use strict';
var PooledClass = require('PooledClass');
var invariant = require('fbjs/lib/invariant');
var validateCallback = require('validateCallback');
/**
* A specialized pseudo-event module to help keep track of components waiting to
* be notified when their DOM representations are available for use.
*
* This implements `PooledClass`, so you should never need to instantiate this.
* Instead, use `CallbackQueue.getPooled()`.
*
* @class CallbackQueue
* @implements PooledClass
* @internal
*/
class CallbackQueue<T> {
_callbacks: ?Array<() => void>;
_contexts: ?Array<T>;
constructor() {
this._callbacks = null;
this._contexts = null;
}
/**
* Enqueues a callback to be invoked when `notifyAll` is invoked.
*
* @param {function} callback Invoked when `notifyAll` is invoked.
* @param {?object} context Context to call `callback` with.
* @internal
*/
enqueue(callback: () => void, context: T) {
this._callbacks = this._callbacks || [];
this._callbacks.push(callback);
this._contexts = this._contexts || [];
this._contexts.push(context);
}
/**
* Invokes all enqueued callbacks and clears the queue. This is invoked after
* the DOM representation of a component has been created or updated.
*
* @internal
*/
notifyAll() {
var callbacks = this._callbacks;
var contexts = this._contexts;
if (callbacks && contexts) {
invariant(
callbacks.length === contexts.length,
'Mismatched list of contexts in callback queue',
);
this._callbacks = null;
this._contexts = null;
for (var i = 0; i < callbacks.length; i++) {
validateCallback(callbacks[i]);
callbacks[i].call(contexts[i]);
}
callbacks.length = 0;
contexts.length = 0;
}
}
checkpoint() {
return this._callbacks ? this._callbacks.length : 0;
}
rollback(len: number) {
if (this._callbacks && this._contexts) {
this._callbacks.length = len;
this._contexts.length = len;
}
}
/**
* Resets the internal queue.
*
* @internal
*/
reset() {
this._callbacks = null;
this._contexts = null;
}
/**
* `PooledClass` looks for this.
*/
destructor() {
this.reset();
}
}
module.exports = PooledClass.addPoolingTo(CallbackQueue);
@@ -1,63 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule KeyEscapeUtils
* @flow
*/
'use strict';
var emptyFunction = require('fbjs/lib/emptyFunction');
/**
* Escape and wrap key so it is safe to use as a reactid
*
* @param {string} key to be escaped.
* @return {string} the escaped key.
*/
function escape(key: string): string {
var escapeRegex = /[=:]/g;
var escaperLookup = {
'=': '=0',
':': '=2',
};
var escapedString = ('' + key).replace(escapeRegex, function(match) {
return escaperLookup[match];
});
return '$' + escapedString;
}
var unescapeInDev = emptyFunction;
if (__DEV__) {
/**
* Unescape and unwrap key for human-readable display
*
* @param {string} key to unescape.
* @return {string} the unescaped key.
*/
unescapeInDev = function(key: string): string {
var unescapeRegex = /(=0|=2)/g;
var unescaperLookup = {
'=0': '=',
'=2': ':',
};
var keySubstring = key[0] === '.' && key[1] === '$'
? key.substring(2)
: key.substring(1);
return ('' + keySubstring).replace(unescapeRegex, function(match) {
return unescaperLookup[match];
});
};
}
var KeyEscapeUtils = {
escape: escape,
unescapeInDev: unescapeInDev,
};
module.exports = KeyEscapeUtils;
@@ -1,212 +0,0 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactChildReconciler
*/
'use strict';
var KeyEscapeUtils = require('KeyEscapeUtils');
var ReactReconciler = require('ReactReconciler');
var instantiateReactComponent = require('instantiateReactComponent');
var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
var traverseStackChildren = require('traverseStackChildren');
if (__DEV__) {
var warning = require('fbjs/lib/warning');
}
var ReactComponentTreeHook;
if (
typeof process !== 'undefined' &&
process.env &&
process.env.NODE_ENV === 'test'
) {
// Temporary hack.
// Inline requires don't work well with Jest:
// https://github.com/facebook/react/issues/7240
// Remove the inline requires when we don't need them anymore:
// https://github.com/facebook/react/pull/7178
ReactComponentTreeHook = require('ReactGlobalSharedState')
.ReactComponentTreeHook;
}
function instantiateChild(childInstances, child, name, selfDebugID) {
// We found a component instance.
var keyUnique = childInstances[name] === undefined;
if (__DEV__) {
if (!ReactComponentTreeHook) {
ReactComponentTreeHook = require('ReactGlobalSharedState')
.ReactComponentTreeHook;
}
if (!keyUnique) {
warning(
false,
'flattenChildren(...): ' +
'Encountered two children with the same key, `%s`. ' +
'Keys should be unique so that components maintain their identity ' +
'across updates. Non-unique keys may cause children to be ' +
'duplicated and/or omitted — the behavior is unsupported and ' +
'could change in a future version.%s',
KeyEscapeUtils.unescapeInDev(name),
ReactComponentTreeHook.getStackAddendumByID(selfDebugID),
);
}
}
if (child != null && keyUnique) {
childInstances[name] = instantiateReactComponent(child, true);
}
}
/**
* ReactChildReconciler provides helpers for initializing or updating a set of
* children. Its output is suitable for passing it onto ReactMultiChild which
* does diffed reordering and insertion.
*/
var ReactChildReconciler = {
/**
* Generates a "mount image" for each of the supplied children. In the case
* of `ReactDOMComponent`, a mount image is a string of markup.
*
* @param {?object} nestedChildNodes Nested child maps.
* @return {?object} A set of child instances.
* @internal
*/
instantiateChildren: function(
nestedChildNodes,
transaction,
context,
selfDebugID, // 0 in production and for roots
) {
if (nestedChildNodes == null) {
return null;
}
var childInstances = {};
if (__DEV__) {
traverseStackChildren(
nestedChildNodes,
(childInsts, child, name) =>
instantiateChild(childInsts, child, name, selfDebugID),
childInstances,
);
} else {
traverseStackChildren(nestedChildNodes, instantiateChild, childInstances);
}
return childInstances;
},
/**
* Updates the rendered children and returns a new set of children.
*
* @param {?object} prevChildren Previously initialized set of children.
* @param {?object} nextChildren Flat child element maps.
* @param {ReactReconcileTransaction} transaction
* @param {object} context
* @return {?object} A new set of child instances.
* @internal
*/
updateChildren: function(
prevChildren,
nextChildren,
mountImages,
removedNodes,
transaction,
hostParent,
hostContainerInfo,
context,
selfDebugID, // 0 in production and for roots
) {
// We currently don't have a way to track moves here but if we use iterators
// instead of for..in we can zip the iterators and check if an item has
// moved.
// TODO: If nothing has changed, return the prevChildren object so that we
// can quickly bailout if nothing has changed.
if (!nextChildren && !prevChildren) {
return;
}
var name;
var prevChild;
for (name in nextChildren) {
if (!nextChildren.hasOwnProperty(name)) {
continue;
}
prevChild = prevChildren && prevChildren[name];
var prevElement = prevChild && prevChild._currentElement;
var nextElement = nextChildren[name];
if (
prevChild != null &&
shouldUpdateReactComponent(prevElement, nextElement)
) {
ReactReconciler.receiveComponent(
prevChild,
nextElement,
transaction,
context,
);
nextChildren[name] = prevChild;
} else {
// The child must be instantiated before it's mounted.
var nextChildInstance = instantiateReactComponent(nextElement, true);
nextChildren[name] = nextChildInstance;
// Creating mount image now ensures refs are resolved in right order
// (see https://github.com/facebook/react/pull/7101 for explanation).
var nextChildMountImage = ReactReconciler.mountComponent(
nextChildInstance,
transaction,
hostParent,
hostContainerInfo,
context,
selfDebugID,
);
mountImages.push(nextChildMountImage);
if (prevChild) {
removedNodes[name] = ReactReconciler.getHostNode(prevChild);
ReactReconciler.unmountComponent(
prevChild,
false /* safely */,
false /* skipLifecycle */,
);
}
}
}
// Unmount children that are no longer present.
for (name in prevChildren) {
if (
prevChildren.hasOwnProperty(name) &&
!(nextChildren && nextChildren.hasOwnProperty(name))
) {
prevChild = prevChildren[name];
removedNodes[name] = ReactReconciler.getHostNode(prevChild);
ReactReconciler.unmountComponent(
prevChild,
false /* safely */,
false /* skipLifecycle */,
);
}
}
},
/**
* Unmounts all rendered children. This should be used to clean up children
* when this component is unmounted.
*
* @param {?object} renderedChildren Previously initialized set of children.
* @internal
*/
unmountChildren: function(renderedChildren, safely, skipLifecycle) {
for (var name in renderedChildren) {
if (renderedChildren.hasOwnProperty(name)) {
var renderedChild = renderedChildren[name];
ReactReconciler.unmountComponent(renderedChild, safely, skipLifecycle);
}
}
},
};
module.exports = ReactChildReconciler;
@@ -1,53 +0,0 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactComponentEnvironment
* @flow
*/
'use strict';
var invariant = require('fbjs/lib/invariant');
type ReplaceNodeWithMarkup = (node: HTMLElement, markup: string) => void;
type ProcessChildrenUpdates = (instance: mixed, updates: mixed) => void;
type Environment = {
replaceNodeWithMarkup: ReplaceNodeWithMarkup,
processChildrenUpdates: ProcessChildrenUpdates,
};
var injected = false;
var ReactComponentEnvironment = {
/**
* Optionally injectable hook for swapping out mount images in the middle of
* the tree.
*/
replaceNodeWithMarkup: (null: ?ReplaceNodeWithMarkup),
/**
* Optionally injectable hook for processing a queue of child updates. Will
* later move into MultiChildComponents.
*/
processChildrenUpdates: (null: ?ReplaceNodeWithMarkup),
injection: {
injectEnvironment: function(environment: Environment) {
invariant(
!injected,
'ReactCompositeComponent: injectEnvironment() can only be called once.',
);
ReactComponentEnvironment.replaceNodeWithMarkup =
environment.replaceNodeWithMarkup;
ReactComponentEnvironment.processChildrenUpdates =
environment.processChildrenUpdates;
injected = true;
},
},
};
module.exports = ReactComponentEnvironment;
File diff suppressed because it is too large Load Diff
@@ -1,17 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactCompositeComponentTypes
* @flow
*/
export type CompositeComponentTypes = 0 | 1 | 2;
module.exports = {
ImpureClass: 0,
PureClass: 1,
StatelessFunctional: 2,
};
@@ -1,36 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactDebugCurrentStack
* @flow
*/
'use strict';
import type {DebugID} from 'ReactInstanceType';
const ReactDebugCurrentStack = {};
if (__DEV__) {
var {ReactComponentTreeHook} = require('ReactGlobalSharedState');
var {getStackAddendumByID, getCurrentStackAddendum} = ReactComponentTreeHook;
// Component that is being worked on
ReactDebugCurrentStack.current = (null: DebugID | null);
ReactDebugCurrentStack.getStackAddendum = function(): string | null {
let stack = null;
const current = ReactDebugCurrentStack.current;
if (current !== null) {
stack = getStackAddendumByID(current);
} else {
stack = getCurrentStackAddendum();
}
return stack;
};
}
module.exports = ReactDebugCurrentStack;
@@ -1,64 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactDefaultBatchingStrategy
*/
'use strict';
var ReactUpdates = require('ReactUpdates');
var Transaction = require('Transaction');
var emptyFunction = require('fbjs/lib/emptyFunction');
var RESET_BATCHED_UPDATES = {
initialize: emptyFunction,
close: function() {
ReactDefaultBatchingStrategy.isBatchingUpdates = false;
},
};
var FLUSH_BATCHED_UPDATES = {
initialize: emptyFunction,
close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates),
};
var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];
function ReactDefaultBatchingStrategyTransaction() {
this.reinitializeTransaction();
}
Object.assign(ReactDefaultBatchingStrategyTransaction.prototype, Transaction, {
getTransactionWrappers: function() {
return TRANSACTION_WRAPPERS;
},
});
var transaction = new ReactDefaultBatchingStrategyTransaction();
var ReactDefaultBatchingStrategy = {
isBatchingUpdates: false,
/**
* Call the provided function in a context within which calls to `setState`
* and friends are batched such that components aren't updated unnecessarily.
*/
batchedUpdates: function(callback, a, b, c, d, e) {
var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
ReactDefaultBatchingStrategy.isBatchingUpdates = true;
// The code is written this way to avoid extra allocations
if (alreadyBatchingUpdates) {
return callback(a, b, c, d, e);
} else {
return transaction.perform(callback, null, a, b, c, d, e);
}
},
};
module.exports = ReactDefaultBatchingStrategy;
@@ -1,28 +0,0 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactEmptyComponent
*/
'use strict';
var emptyComponentFactory;
var ReactEmptyComponentInjection = {
injectEmptyComponentFactory: function(factory) {
emptyComponentFactory = factory;
},
};
var ReactEmptyComponent = {
create: function(instantiate) {
return emptyComponentFactory(instantiate);
},
};
ReactEmptyComponent.injection = ReactEmptyComponentInjection;
module.exports = ReactEmptyComponent;
@@ -1,68 +0,0 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactHostComponent
*/
'use strict';
var invariant = require('fbjs/lib/invariant');
var genericComponentClass = null;
var textComponentClass = null;
var ReactHostComponentInjection = {
// This accepts a class that receives the tag string. This is a catch all
// that can render any kind of tag.
injectGenericComponentClass: function(componentClass) {
genericComponentClass = componentClass;
},
// This accepts a text component class that takes the text string to be
// rendered as props.
injectTextComponentClass: function(componentClass) {
textComponentClass = componentClass;
},
};
/**
* Get a host internal component class for a specific tag.
*
* @param {ReactElement} element The element to create.
* @return {function} The internal class constructor function.
*/
function createInternalComponent(element) {
invariant(
genericComponentClass,
'There is no registered component for the tag %s',
element.type,
);
return new genericComponentClass(element);
}
/**
* @param {ReactText} text
* @return {ReactComponent}
*/
function createInstanceForText(text) {
return new textComponentClass(text);
}
/**
* @param {ReactComponent} component
* @return {boolean}
*/
function isTextComponent(component) {
return component instanceof textComponentClass;
}
var ReactHostComponent = {
createInternalComponent: createInternalComponent,
createInstanceForText: createInstanceForText,
isTextComponent: isTextComponent,
injection: ReactHostComponentInjection,
};
module.exports = ReactHostComponent;
@@ -1,47 +0,0 @@
/**
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactInstanceType
* @flow
*/
'use strict';
import type {ReactElement} from 'ReactElementType';
import type {CompositeComponentTypes} from 'ReactCompositeComponentTypes';
export type DebugID = number;
export type ReactInstance = {
// Shared
mountComponent: any,
unmountComponent: any,
receiveComponent: any,
getName: () => string,
getPublicInstance: any,
_currentElement: ReactElement,
// ReactCompositeComponent
performInitialMountWithErrorHandling: any,
performInitialMount: any,
getHostNode: any,
performUpdateIfNecessary: any,
updateComponent: any,
attachRef: (ref: string, component: ReactInstance) => void,
detachRef: (ref: string) => void,
_rootNodeID: number,
_compositeType: CompositeComponentTypes,
// ReactDOMComponent
_tag: string,
// instantiateReactComponent
_mountIndex: number,
_mountImage: any,
// __DEV__
_debugID: DebugID,
_warnedAboutRefsInRender: boolean,
};
@@ -1,544 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactMultiChild
*/
'use strict';
var ReactComponentEnvironment = require('ReactComponentEnvironment');
var ReactInstanceMap = require('ReactInstanceMap');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactReconciler = require('ReactReconciler');
var ReactChildReconciler = require('ReactChildReconciler');
var {ReactCurrentOwner} = require('ReactGlobalSharedState');
if (__DEV__) {
var {ReactDebugCurrentFrame} = require('ReactGlobalSharedState');
var ReactDebugCurrentStack = require('ReactDebugCurrentStack');
}
var emptyFunction = require('fbjs/lib/emptyFunction');
var flattenStackChildren = require('flattenStackChildren');
var invariant = require('fbjs/lib/invariant');
/**
* Make an update for markup to be rendered and inserted at a supplied index.
*
* @param {string} markup Markup that renders into an element.
* @param {number} toIndex Destination index.
* @private
*/
function makeInsertMarkup(markup, afterNode, toIndex) {
// NOTE: Null values reduce hidden classes.
return {
type: 'INSERT_MARKUP',
content: markup,
fromIndex: null,
fromNode: null,
toIndex: toIndex,
afterNode: afterNode,
};
}
/**
* Make an update for moving an existing element to another index.
*
* @param {number} fromIndex Source index of the existing element.
* @param {number} toIndex Destination index of the element.
* @private
*/
function makeMove(child, afterNode, toIndex) {
// NOTE: Null values reduce hidden classes.
return {
type: 'MOVE_EXISTING',
content: null,
fromIndex: child._mountIndex,
fromNode: ReactReconciler.getHostNode(child),
toIndex: toIndex,
afterNode: afterNode,
};
}
/**
* Make an update for removing an element at an index.
*
* @param {number} fromIndex Index of the element to remove.
* @private
*/
function makeRemove(child, node) {
// NOTE: Null values reduce hidden classes.
return {
type: 'REMOVE_NODE',
content: null,
fromIndex: child._mountIndex,
fromNode: node,
toIndex: null,
afterNode: null,
};
}
/**
* Make an update for setting the markup of a node.
*
* @param {string} markup Markup that renders into an element.
* @private
*/
function makeSetMarkup(markup) {
// NOTE: Null values reduce hidden classes.
return {
type: 'SET_MARKUP',
content: markup,
fromIndex: null,
fromNode: null,
toIndex: null,
afterNode: null,
};
}
/**
* Make an update for setting the text content.
*
* @param {string} textContent Text content to set.
* @private
*/
function makeTextContent(textContent) {
// NOTE: Null values reduce hidden classes.
return {
type: 'TEXT_CONTENT',
content: textContent,
fromIndex: null,
fromNode: null,
toIndex: null,
afterNode: null,
};
}
/**
* Push an update, if any, onto the queue. Creates a new queue if none is
* passed and always returns the queue. Mutative.
*/
function enqueue(queue, update) {
if (update) {
queue = queue || [];
queue.push(update);
}
return queue;
}
/**
* Processes any enqueued updates.
*
* @private
*/
function processQueue(inst, updateQueue) {
ReactComponentEnvironment.processChildrenUpdates(inst, updateQueue);
}
var setChildrenForInstrumentation = emptyFunction;
if (__DEV__) {
var getDebugID = function(inst) {
if (!inst._debugID) {
// Check for ART-like instances. TODO: This is silly/gross.
var internal;
if ((internal = ReactInstanceMap.get(inst))) {
inst = internal;
}
}
return inst._debugID;
};
setChildrenForInstrumentation = function(children) {
var debugID = getDebugID(this);
// TODO: React Native empty components are also multichild.
// This means they still get into this method but don't have _debugID.
if (debugID !== 0) {
ReactInstrumentation.debugTool.onSetChildren(
debugID,
children
? Object.keys(children).map(key => children[key]._debugID)
: [],
);
}
};
}
/**
* Provides common functionality for components that must reconcile multiple
* children. This is used by `ReactDOMComponent` to mount, update, and
* unmount child components.
*/
var ReactMultiChild = {
_reconcilerInstantiateChildren: function(
nestedChildren,
transaction,
context,
) {
if (__DEV__) {
var selfDebugID = getDebugID(this);
if (this._currentElement) {
try {
ReactCurrentOwner.current = this._currentElement._owner;
ReactDebugCurrentFrame.getCurrentStack =
ReactDebugCurrentStack.getStackAddendum;
return ReactChildReconciler.instantiateChildren(
nestedChildren,
transaction,
context,
selfDebugID,
);
} finally {
ReactCurrentOwner.current = null;
ReactDebugCurrentFrame.getCurrentStack = null;
}
}
}
return ReactChildReconciler.instantiateChildren(
nestedChildren,
transaction,
context,
);
},
_reconcilerUpdateChildren: function(
prevChildren,
nextNestedChildrenElements,
mountImages,
removedNodes,
transaction,
context,
) {
var nextChildren;
var selfDebugID = 0;
if (__DEV__) {
selfDebugID = getDebugID(this);
if (this._currentElement) {
try {
ReactCurrentOwner.current = this._currentElement._owner;
ReactDebugCurrentFrame.getCurrentStack =
ReactDebugCurrentStack.getStackAddendum;
nextChildren = flattenStackChildren(
nextNestedChildrenElements,
selfDebugID,
);
} finally {
ReactCurrentOwner.current = null;
ReactDebugCurrentFrame.getCurrentStack = null;
}
ReactChildReconciler.updateChildren(
prevChildren,
nextChildren,
mountImages,
removedNodes,
transaction,
this,
this._hostContainerInfo,
context,
selfDebugID,
);
return nextChildren;
}
}
nextChildren = flattenStackChildren(
nextNestedChildrenElements,
selfDebugID,
);
ReactChildReconciler.updateChildren(
prevChildren,
nextChildren,
mountImages,
removedNodes,
transaction,
this,
this._hostContainerInfo,
context,
selfDebugID,
);
return nextChildren;
},
/**
* Generates a "mount image" for each of the supplied children. In the case
* of `ReactDOMComponent`, a mount image is a string of markup.
*
* @param {?object} nestedChildren Nested child maps.
* @return {array} An array of mounted representations.
* @internal
*/
mountChildren: function(nestedChildren, transaction, context) {
var children = this._reconcilerInstantiateChildren(
nestedChildren,
transaction,
context,
);
this._renderedChildren = children;
var mountImages = [];
var index = 0;
for (var name in children) {
if (children.hasOwnProperty(name)) {
var child = children[name];
var selfDebugID = 0;
if (__DEV__) {
selfDebugID = getDebugID(this);
}
var mountImage = ReactReconciler.mountComponent(
child,
transaction,
this,
this._hostContainerInfo,
context,
selfDebugID,
);
child._mountIndex = index++;
mountImages.push(mountImage);
}
}
if (__DEV__) {
setChildrenForInstrumentation.call(this, children);
}
return mountImages;
},
/**
* Replaces any rendered children with a text content string.
*
* @param {string} nextContent String of content.
* @internal
*/
updateTextContent: function(nextContent) {
var prevChildren = this._renderedChildren;
// Remove any rendered children.
ReactChildReconciler.unmountChildren(
prevChildren,
false /* safely */,
false /* skipLifecycle */,
);
for (var name in prevChildren) {
if (prevChildren.hasOwnProperty(name)) {
invariant(false, 'updateTextContent called on non-empty component.');
}
}
// Set new text content.
var updates = [makeTextContent(nextContent)];
processQueue(this, updates);
},
/**
* Replaces any rendered children with a markup string.
*
* @param {string} nextMarkup String of markup.
* @internal
*/
updateMarkup: function(nextMarkup) {
var prevChildren = this._renderedChildren;
// Remove any rendered children.
ReactChildReconciler.unmountChildren(
prevChildren,
false /* safely */,
false /* skipLifecycle */,
);
for (var name in prevChildren) {
if (prevChildren.hasOwnProperty(name)) {
invariant(false, 'updateTextContent called on non-empty component.');
}
}
var updates = [makeSetMarkup(nextMarkup)];
processQueue(this, updates);
},
/**
* Updates the rendered children with new children.
*
* @param {?object} nextNestedChildrenElements Nested child element maps.
* @param {ReactReconcileTransaction} transaction
* @internal
*/
updateChildren: function(nextNestedChildrenElements, transaction, context) {
// Hook used by React ART
this._updateChildren(nextNestedChildrenElements, transaction, context);
},
/**
* @param {?object} nextNestedChildrenElements Nested child element maps.
* @param {ReactReconcileTransaction} transaction
* @final
* @protected
*/
_updateChildren: function(nextNestedChildrenElements, transaction, context) {
var prevChildren = this._renderedChildren;
var removedNodes = {};
var mountImages = [];
var nextChildren = this._reconcilerUpdateChildren(
prevChildren,
nextNestedChildrenElements,
mountImages,
removedNodes,
transaction,
context,
);
if (!nextChildren && !prevChildren) {
return;
}
var updates = null;
var name;
// `nextIndex` will increment for each child in `nextChildren`, but
// `lastIndex` will be the last index visited in `prevChildren`.
var nextIndex = 0;
var lastIndex = 0;
// `nextMountIndex` will increment for each newly mounted child.
var nextMountIndex = 0;
var lastPlacedNode = null;
for (name in nextChildren) {
if (!nextChildren.hasOwnProperty(name)) {
continue;
}
var prevChild = prevChildren && prevChildren[name];
var nextChild = nextChildren[name];
if (prevChild === nextChild) {
updates = enqueue(
updates,
this.moveChild(prevChild, lastPlacedNode, nextIndex, lastIndex),
);
lastIndex = Math.max(prevChild._mountIndex, lastIndex);
prevChild._mountIndex = nextIndex;
} else {
if (prevChild) {
// Update `lastIndex` before `_mountIndex` gets unset by unmounting.
lastIndex = Math.max(prevChild._mountIndex, lastIndex);
// The `removedNodes` loop below will actually remove the child.
}
// The child must be instantiated before it's mounted.
updates = enqueue(
updates,
this._mountChildAtIndex(
nextChild,
mountImages[nextMountIndex],
lastPlacedNode,
nextIndex,
transaction,
context,
),
);
nextMountIndex++;
}
nextIndex++;
lastPlacedNode = ReactReconciler.getHostNode(nextChild);
}
// Remove children that are no longer present.
for (name in removedNodes) {
if (removedNodes.hasOwnProperty(name)) {
updates = enqueue(
updates,
this._unmountChild(prevChildren[name], removedNodes[name]),
);
}
}
if (updates) {
processQueue(this, updates);
}
this._renderedChildren = nextChildren;
if (__DEV__) {
setChildrenForInstrumentation.call(this, nextChildren);
}
},
/**
* Unmounts all rendered children. This should be used to clean up children
* when this component is unmounted. It does not actually perform any
* backend operations.
*
* @internal
*/
unmountChildren: function(safely, skipLifecycle) {
var renderedChildren = this._renderedChildren;
ReactChildReconciler.unmountChildren(
renderedChildren,
safely,
skipLifecycle,
);
this._renderedChildren = null;
},
/**
* Moves a child component to the supplied index.
*
* @param {ReactComponent} child Component to move.
* @param {number} toIndex Destination index of the element.
* @param {number} lastIndex Last index visited of the siblings of `child`.
* @protected
*/
moveChild: function(child, afterNode, toIndex, lastIndex) {
// If the index of `child` is less than `lastIndex`, then it needs to
// be moved. Otherwise, we do not need to move it because a child will be
// inserted or moved before `child`.
if (child._mountIndex < lastIndex) {
return makeMove(child, afterNode, toIndex);
}
},
/**
* Creates a child component.
*
* @param {ReactComponent} child Component to create.
* @param {string} mountImage Markup to insert.
* @protected
*/
createChild: function(child, afterNode, mountImage) {
return makeInsertMarkup(mountImage, afterNode, child._mountIndex);
},
/**
* Removes a child component.
*
* @param {ReactComponent} child Child to remove.
* @protected
*/
removeChild: function(child, node) {
return makeRemove(child, node);
},
/**
* Mounts a child with the supplied name.
*
* NOTE: This is part of `updateChildren` and is here for readability.
*
* @param {ReactComponent} child Component to mount.
* @param {string} name Name of the child.
* @param {number} index Index at which to insert the child.
* @param {ReactReconcileTransaction} transaction
* @private
*/
_mountChildAtIndex: function(
child,
mountImage,
afterNode,
index,
transaction,
context,
) {
child._mountIndex = index;
return this.createChild(child, afterNode, mountImage);
},
/**
* Unmounts a rendered child.
*
* NOTE: This is part of `updateChildren` and is here for readability.
*
* @param {ReactComponent} child Component to unmount.
* @private
*/
_unmountChild: function(child, node) {
var update = this.removeChild(child, node);
child._mountIndex = null;
return update;
},
};
module.exports = ReactMultiChild;
@@ -1,23 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactMultiChildUpdateTypes
*/
'use strict';
/**
* When a component's children are updated, a series of update configuration
* objects are created in order to batch and serialize the required changes.
*
* Enumerates all the possible types of update configurations.
*/
export type ReactMultiChildUpdateTypes =
| 'INSERT_MARKUP'
| 'MOVE_EXISTING'
| 'REMOVE_NODE'
| 'SET_MARKUP'
| 'TEXT_CONTENT';
@@ -1,38 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactNodeTypes
* @flow
*/
'use strict';
type ReactNodeType = 0 | 1 | 2;
var React = require('react');
var invariant = require('fbjs/lib/invariant');
var ReactNodeTypes = {
HOST: 0,
COMPOSITE: 1,
EMPTY: 2,
getType: function(node: React$Element<any>): ReactNodeType {
if (node === null || node === false) {
return ReactNodeTypes.EMPTY;
} else if (React.isValidElement(node)) {
if (typeof node.type === 'function') {
return ReactNodeTypes.COMPOSITE;
} else {
return ReactNodeTypes.HOST;
}
}
invariant(false, 'Unexpected node: %s', node);
},
};
module.exports = ReactNodeTypes;
@@ -1,133 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactOwner
* @flow
*/
'use strict';
var {ClassComponent} = require('ReactTypeOfWork');
var emptyObject = require('fbjs/lib/emptyObject');
var invariant = require('fbjs/lib/invariant');
import type {Fiber} from 'ReactFiber';
import type {ReactInstance} from 'ReactInstanceType';
/**
* @param {?object} object
* @return {boolean} True if `object` is a valid owner.
* @final
*/
function isValidOwner(object: any): boolean {
return !!(object &&
typeof object.attachRef === 'function' &&
typeof object.detachRef === 'function');
}
/**
* ReactOwners are capable of storing references to owned components.
*
* All components are capable of //being// referenced by owner components, but
* only ReactOwner components are capable of //referencing// owned components.
* The named reference is known as a "ref".
*
* Refs are available when mounted and updated during reconciliation.
*
* var MyComponent = React.createClass({
* render: function() {
* return (
* <div onClick={this.handleClick}>
* <CustomComponent ref="custom" />
* </div>
* );
* },
* handleClick: function() {
* this.refs.custom.handleClick();
* },
* componentDidMount: function() {
* this.refs.custom.initialize();
* }
* });
*
* Refs should rarely be used. When refs are used, they should only be done to
* control data that is not handled by React's data flow.
*
* @class ReactOwner
*/
var ReactOwner = {
/**
* Adds a component by ref to an owner component.
*
* @param {ReactComponent} component Component to reference.
* @param {string} ref Name by which to refer to the component.
* @param {ReactOwner} owner Component on which to record the ref.
* @final
* @internal
*/
addComponentAsRefTo: function(
component: ReactInstance,
ref: string,
owner: ReactInstance | Fiber,
): void {
if (owner && (owner: any).tag === ClassComponent) {
const inst = (owner: any).stateNode;
const refs = inst.refs === emptyObject ? (inst.refs = {}) : inst.refs;
refs[ref] = component.getPublicInstance();
} else {
invariant(
isValidOwner(owner),
'addComponentAsRefTo(...): Only a ReactOwner can have refs. You might ' +
"be adding a ref to a component that was not created inside a component's " +
'`render` method, or you have multiple copies of React loaded ' +
'(details: https://fb.me/react-refs-must-have-owner).',
);
(owner: any).attachRef(ref, component);
}
},
/**
* Removes a component by ref from an owner component.
*
* @param {ReactComponent} component Component to dereference.
* @param {string} ref Name of the ref to remove.
* @param {ReactOwner} owner Component on which the ref is recorded.
* @final
* @internal
*/
removeComponentAsRefFrom: function(
component: ReactInstance,
ref: string,
owner: ReactInstance | Fiber,
): void {
if (owner && (owner: any).tag === ClassComponent) {
const inst = (owner: any).stateNode;
if (inst && inst.refs[ref] === component.getPublicInstance()) {
delete inst.refs[ref];
}
} else {
invariant(
isValidOwner(owner),
'removeComponentAsRefFrom(...): Only a ReactOwner can have refs. You might ' +
"be removing a ref to a component that was not created inside a component's " +
'`render` method, or you have multiple copies of React loaded ' +
'(details: https://fb.me/react-refs-must-have-owner).',
);
var ownerPublicInstance = (owner: any).getPublicInstance();
// Check that `component`'s owner is still alive and that `component` is still the current ref
// because we do not want to detach the ref if another component stole it.
if (
ownerPublicInstance &&
ownerPublicInstance.refs[ref] === component.getPublicInstance()
) {
(owner: any).detachRef(ref);
}
}
},
};
module.exports = ReactOwner;
@@ -1,223 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactReconciler
*/
'use strict';
var ReactRef = require('ReactRef');
var ReactInstrumentation = require('ReactInstrumentation');
if (__DEV__) {
var warning = require('fbjs/lib/warning');
}
/**
* Helper to call ReactRef.attachRefs with this composite component, split out
* to avoid allocations in the transaction mount-ready queue.
*/
function attachRefs() {
ReactRef.attachRefs(this, this._currentElement);
}
var ReactReconciler = {
/**
* Initializes the component, renders markup, and registers event listeners.
*
* @param {ReactComponent} internalInstance
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
* @param {?object} hostParent the containing host component instance
* @param {?object} hostContainerInfo info about the host container
* @return {?string} Rendered markup to be inserted into the DOM.
* @final
* @internal
*/
mountComponent: function(
internalInstance,
transaction,
hostParent,
hostContainerInfo,
context,
parentDebugID, // 0 in production and for roots
) {
if (__DEV__) {
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onBeforeMountComponent(
internalInstance._debugID,
internalInstance._currentElement,
parentDebugID,
);
}
}
var markup = internalInstance.mountComponent(
transaction,
hostParent,
hostContainerInfo,
context,
parentDebugID,
);
if (
internalInstance._currentElement &&
internalInstance._currentElement.ref != null
) {
transaction.getReactMountReady().enqueue(attachRefs, internalInstance);
}
if (__DEV__) {
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onMountComponent(
internalInstance._debugID,
);
}
}
return markup;
},
/**
* Returns a value that can be passed to
* ReactComponentEnvironment.replaceNodeWithMarkup.
*/
getHostNode: function(internalInstance) {
return internalInstance.getHostNode();
},
/**
* Releases any resources allocated by `mountComponent`.
*
* @final
* @internal
*/
unmountComponent: function(internalInstance, safely, skipLifecycle) {
if (__DEV__) {
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onBeforeUnmountComponent(
internalInstance._debugID,
);
}
}
ReactRef.detachRefs(internalInstance, internalInstance._currentElement);
internalInstance.unmountComponent(safely, skipLifecycle);
if (__DEV__) {
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onUnmountComponent(
internalInstance._debugID,
);
}
}
},
/**
* Update a component using a new element.
*
* @param {ReactComponent} internalInstance
* @param {ReactElement} nextElement
* @param {ReactReconcileTransaction} transaction
* @param {object} context
* @internal
*/
receiveComponent: function(
internalInstance,
nextElement,
transaction,
context,
) {
var prevElement = internalInstance._currentElement;
if (nextElement === prevElement && context === internalInstance._context) {
// Since elements are immutable after the owner is rendered,
// we can do a cheap identity compare here to determine if this is a
// superfluous reconcile. It's possible for state to be mutable but such
// change should trigger an update of the owner which would recreate
// the element. We explicitly check for the existence of an owner since
// it's possible for an element created outside a composite to be
// deeply mutated and reused.
// TODO: Bailing out early is just a perf optimization right?
// TODO: Removing the return statement should affect correctness?
return;
}
if (__DEV__) {
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onBeforeUpdateComponent(
internalInstance._debugID,
nextElement,
);
}
}
var refsChanged = ReactRef.shouldUpdateRefs(prevElement, nextElement);
if (refsChanged) {
ReactRef.detachRefs(internalInstance, prevElement);
}
internalInstance.receiveComponent(nextElement, transaction, context);
if (
refsChanged &&
internalInstance._currentElement &&
internalInstance._currentElement.ref != null
) {
transaction.getReactMountReady().enqueue(attachRefs, internalInstance);
}
if (__DEV__) {
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onUpdateComponent(
internalInstance._debugID,
);
}
}
},
/**
* Flush any dirty changes in a component.
*
* @param {ReactComponent} internalInstance
* @param {ReactReconcileTransaction} transaction
* @internal
*/
performUpdateIfNecessary: function(
internalInstance,
transaction,
updateBatchNumber,
) {
if (internalInstance._updateBatchNumber !== updateBatchNumber) {
if (__DEV__) {
// The component's enqueued batch number should always be the current
// batch or the following one.
warning(
internalInstance._updateBatchNumber == null ||
internalInstance._updateBatchNumber === updateBatchNumber + 1,
'performUpdateIfNecessary: Unexpected batch number (current %s, ' +
'pending %s)',
updateBatchNumber,
internalInstance._updateBatchNumber,
);
}
return;
}
if (__DEV__) {
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onBeforeUpdateComponent(
internalInstance._debugID,
internalInstance._currentElement,
);
}
}
internalInstance.performUpdateIfNecessary(transaction);
if (__DEV__) {
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onUpdateComponent(
internalInstance._debugID,
);
}
}
},
};
module.exports = ReactReconciler;
@@ -1,144 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactRef
* @flow
*/
'use strict';
var ReactOwner = require('ReactOwner');
import type {ReactInstance} from 'ReactInstanceType';
import type {ReactElement} from 'ReactElementType';
var ReactRef = {};
if (__DEV__) {
var ReactCompositeComponentTypes = require('ReactCompositeComponentTypes');
var {ReactComponentTreeHook} = require('ReactGlobalSharedState');
var warning = require('fbjs/lib/warning');
var warnedAboutStatelessRefs = {};
}
function attachRef(ref, component, owner) {
if (__DEV__) {
if (
component._compositeType ===
ReactCompositeComponentTypes.StatelessFunctional
) {
let info = '';
let ownerName;
if (owner) {
if (typeof owner.getName === 'function') {
ownerName = owner.getName();
}
if (ownerName) {
info += '\n\nCheck the render method of `' + ownerName + '`.';
}
}
let warningKey = ownerName || component._debugID;
let element = component._currentElement;
if (element && element._source) {
warningKey =
element._source.fileName + ':' + element._source.lineNumber;
}
if (!warnedAboutStatelessRefs[warningKey]) {
warnedAboutStatelessRefs[warningKey] = true;
warning(
false,
'Stateless function components cannot be given refs. ' +
'Attempts to access this ref will fail.%s%s',
info,
ReactComponentTreeHook.getStackAddendumByID(component._debugID),
);
}
}
}
if (typeof ref === 'function') {
ref(component.getPublicInstance());
} else {
// Legacy ref
ReactOwner.addComponentAsRefTo(component, ref, owner);
}
}
function detachRef(ref, component, owner) {
if (typeof ref === 'function') {
ref(null);
} else {
// Legacy ref
ReactOwner.removeComponentAsRefFrom(component, ref, owner);
}
}
ReactRef.attachRefs = function(
instance: ReactInstance,
element: ReactElement | string | number | null | false,
): void {
if (element === null || typeof element !== 'object') {
return;
}
var ref = element.ref;
if (ref != null) {
attachRef(ref, instance, element._owner);
}
};
ReactRef.shouldUpdateRefs = function(
prevElement: ReactElement | string | number | null | false,
nextElement: ReactElement | string | number | null | false,
): boolean {
// If either the owner or a `ref` has changed, make sure the newest owner
// has stored a reference to `this`, and the previous owner (if different)
// has forgotten the reference to `this`. We use the element instead
// of the public this.props because the post processing cannot determine
// a ref. The ref conceptually lives on the element.
// TODO: Should this even be possible? The owner cannot change because
// it's forbidden by shouldUpdateReactComponent. The ref can change
// if you swap the keys of but not the refs. Reconsider where this check
// is made. It probably belongs where the key checking and
// instantiateReactComponent is done.
var prevRef = null;
var prevOwner = null;
if (prevElement !== null && typeof prevElement === 'object') {
prevRef = prevElement.ref;
prevOwner = prevElement._owner;
}
var nextRef = null;
var nextOwner = null;
if (nextElement !== null && typeof nextElement === 'object') {
nextRef = nextElement.ref;
nextOwner = nextElement._owner;
}
return (
prevRef !== nextRef ||
// If owner changes but we have an unchanged function ref, don't update refs
(typeof nextRef === 'string' && nextOwner !== prevOwner)
);
};
ReactRef.detachRefs = function(
instance: ReactInstance,
element: ReactElement | string | number | null | false,
): void {
if (element === null || typeof element !== 'object') {
return;
}
var ref = element.ref;
if (ref != null) {
detachRef(ref, instance, element._owner);
}
};
module.exports = ReactRef;
@@ -1,49 +0,0 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactSimpleEmptyComponent
*/
'use strict';
var ReactReconciler = require('ReactReconciler');
var ReactSimpleEmptyComponent = function(placeholderElement, instantiate) {
this._currentElement = null;
this._renderedComponent = instantiate(placeholderElement);
};
Object.assign(ReactSimpleEmptyComponent.prototype, {
mountComponent: function(
transaction,
hostParent,
hostContainerInfo,
context,
parentDebugID, // 0 in production and for roots
) {
return ReactReconciler.mountComponent(
this._renderedComponent,
transaction,
hostParent,
hostContainerInfo,
context,
parentDebugID,
);
},
receiveComponent: function() {},
getHostNode: function() {
return ReactReconciler.getHostNode(this._renderedComponent);
},
unmountComponent: function(safely, skipLifecycle) {
ReactReconciler.unmountComponent(
this._renderedComponent,
safely,
skipLifecycle,
);
this._renderedComponent = null;
},
});
module.exports = ReactSimpleEmptyComponent;
@@ -1,255 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactUpdateQueue
*/
'use strict';
var ReactInstanceMap = require('ReactInstanceMap');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactUpdates = require('ReactUpdates');
var {ReactCurrentOwner} = require('ReactGlobalSharedState');
if (__DEV__) {
var warning = require('fbjs/lib/warning');
var warnOnInvalidCallback = function(callback: mixed, callerName: string) {
warning(
callback === null || typeof callback === 'function',
'%s(...): Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
callerName,
'' + callback,
);
};
}
function enqueueUpdate(internalInstance) {
ReactUpdates.enqueueUpdate(internalInstance);
}
function getInternalInstanceReadyForUpdate(publicInstance, callerName) {
var internalInstance = ReactInstanceMap.get(publicInstance);
if (!internalInstance) {
if (__DEV__) {
var ctor = publicInstance.constructor;
warning(
false,
'Can only update a mounted or mounting component. This usually means ' +
'you called setState, replaceState, or forceUpdate on an unmounted ' +
'component. This is a no-op.\n\nPlease check the code for the ' +
'%s component.',
(ctor && (ctor.displayName || ctor.name)) || 'ReactClass',
);
}
return null;
}
if (__DEV__) {
warning(
ReactCurrentOwner.current == null,
'Cannot update during an existing state transition (such as within ' +
"`render` or another component's constructor). Render methods should " +
'be a pure function of props and state; constructor side-effects are ' +
'an anti-pattern, but can be moved to `componentWillMount`.',
);
}
return internalInstance;
}
/**
* ReactUpdateQueue allows for state updates to be scheduled into a later
* reconciliation step.
*/
var ReactUpdateQueue = {
/**
* Checks whether or not this composite component is mounted.
* @param {ReactClass} publicInstance The instance we want to test.
* @return {boolean} True if mounted, false otherwise.
* @protected
* @final
*/
isMounted: function(publicInstance) {
if (__DEV__) {
var owner = ReactCurrentOwner.current;
if (owner !== null) {
warning(
owner._warnedAboutRefsInRender,
'%s is accessing isMounted inside its render() function. ' +
'render() should be a pure function of props and state. It should ' +
'never access something that requires stale data from the previous ' +
'render, such as refs. Move this logic to componentDidMount and ' +
'componentDidUpdate instead.',
owner.getName() || 'A component',
);
owner._warnedAboutRefsInRender = true;
}
}
var internalInstance = ReactInstanceMap.get(publicInstance);
if (internalInstance) {
// During componentWillMount and render this will still be null but after
// that will always render to something. At least for now. So we can use
// this hack.
return !!internalInstance._renderedComponent;
} else {
return false;
}
},
enqueueCallbackInternal: function(internalInstance, callback) {
if (internalInstance._pendingCallbacks) {
internalInstance._pendingCallbacks.push(callback);
} else {
internalInstance._pendingCallbacks = [callback];
}
enqueueUpdate(internalInstance);
},
/**
* Forces an update. This should only be invoked when it is known with
* certainty that we are **not** in a DOM transaction.
*
* You may want to call this when you know that some deeper aspect of the
* component's state has changed but `setState` was not called.
*
* This will not invoke `shouldComponentUpdate`, but it will invoke
* `componentWillUpdate` and `componentDidUpdate`.
*
* @param {ReactClass} publicInstance The instance that should rerender.
* @param {?function} callback Called after component is updated.
* @param {?string} callerName Name of the calling function in the public API.
* @internal
*/
enqueueForceUpdate: function(publicInstance, callback, callerName) {
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
if (!internalInstance) {
return;
}
callback = callback === undefined ? null : callback;
if (callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, callerName);
}
if (internalInstance._pendingCallbacks) {
internalInstance._pendingCallbacks.push(callback);
} else {
internalInstance._pendingCallbacks = [callback];
}
}
internalInstance._pendingForceUpdate = true;
enqueueUpdate(internalInstance);
},
/**
* Replaces all of the state. Always use this or `setState` to mutate state.
* You should treat `this.state` as immutable.
*
* There is no guarantee that `this.state` will be immediately updated, so
* accessing `this.state` after calling this method may return the old value.
*
* @param {ReactClass} publicInstance The instance that should rerender.
* @param {object} completeState Next state.
* @param {?function} callback Called after state is updated.
* @param {?string} Name of the calling function in the public API.
* @internal
*/
enqueueReplaceState: function(
publicInstance,
completeState,
callback,
callerName,
) {
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
if (!internalInstance) {
return;
}
internalInstance._pendingStateQueue = [completeState];
internalInstance._pendingReplaceState = true;
callback = callback === undefined ? null : callback;
if (callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, callerName);
}
if (internalInstance._pendingCallbacks) {
internalInstance._pendingCallbacks.push(callback);
} else {
internalInstance._pendingCallbacks = [callback];
}
}
enqueueUpdate(internalInstance);
},
/**
* Sets a subset of the state. This only exists because _pendingState is
* internal. This provides a merging strategy that is not available to deep
* properties which is confusing. TODO: Expose pendingState or don't use it
* during the merge.
*
* @param {ReactClass} publicInstance The instance that should rerender.
* @param {object} partialState Next partial state to be merged with state.
* @param {?function} callback Called after state is updated.
* @param {?string} Name of the calling function in the public API.
* @internal
*/
enqueueSetState: function(
publicInstance,
partialState,
callback,
callerName,
) {
if (__DEV__) {
ReactInstrumentation.debugTool.onSetState();
warning(
partialState != null,
'setState(...): You passed an undefined or null state object; ' +
'instead, use forceUpdate().',
);
}
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
if (!internalInstance) {
return;
}
var queue =
internalInstance._pendingStateQueue ||
(internalInstance._pendingStateQueue = []);
queue.push(partialState);
callback = callback === undefined ? null : callback;
if (callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, callerName);
}
if (internalInstance._pendingCallbacks) {
internalInstance._pendingCallbacks.push(callback);
} else {
internalInstance._pendingCallbacks = [callback];
}
}
enqueueUpdate(internalInstance);
},
enqueueElementInternal: function(internalInstance, nextElement, nextContext) {
internalInstance._pendingElement = nextElement;
// TODO: introduce _pendingContext instead of setting it directly.
internalInstance._context = nextContext;
enqueueUpdate(internalInstance);
},
};
module.exports = ReactUpdateQueue;
@@ -1,220 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactUpdates
*/
'use strict';
var PooledClass = require('PooledClass');
var ReactReconciler = require('ReactReconciler');
var Transaction = require('Transaction');
var invariant = require('fbjs/lib/invariant');
var dirtyComponents = [];
var updateBatchNumber = 0;
var batchingStrategy = null;
function ensureInjected() {
invariant(
ReactUpdates.ReactReconcileTransaction && batchingStrategy,
'ReactUpdates: must inject a reconcile transaction class and batching ' +
'strategy',
);
}
var NESTED_UPDATES = {
initialize: function() {
this.dirtyComponentsLength = dirtyComponents.length;
},
close: function() {
if (this.dirtyComponentsLength !== dirtyComponents.length) {
// Additional updates were enqueued by componentDidUpdate handlers or
// similar; before our own UPDATE_QUEUEING wrapper closes, we want to run
// these new updates so that if A's componentDidUpdate calls setState on
// B, B will update before the callback A's updater provided when calling
// setState.
dirtyComponents.splice(0, this.dirtyComponentsLength);
flushBatchedUpdates();
} else {
dirtyComponents.length = 0;
}
},
};
var TRANSACTION_WRAPPERS = [NESTED_UPDATES];
function ReactUpdatesFlushTransaction() {
this.reinitializeTransaction();
this.dirtyComponentsLength = null;
this.reconcileTransaction = ReactUpdates.ReactReconcileTransaction.getPooled(
/* useCreateElement */ true,
);
}
Object.assign(ReactUpdatesFlushTransaction.prototype, Transaction, {
getTransactionWrappers: function() {
return TRANSACTION_WRAPPERS;
},
destructor: function() {
this.dirtyComponentsLength = null;
ReactUpdates.ReactReconcileTransaction.release(this.reconcileTransaction);
this.reconcileTransaction = null;
},
perform: function(method, scope, a) {
// Essentially calls `this.reconcileTransaction.perform(method, scope, a)`
// with this transaction's wrappers around it.
return Transaction.perform.call(
this,
this.reconcileTransaction.perform,
this.reconcileTransaction,
method,
scope,
a,
);
},
});
PooledClass.addPoolingTo(ReactUpdatesFlushTransaction);
function batchedUpdates(callback, a, b, c, d, e) {
ensureInjected();
return batchingStrategy.batchedUpdates(callback, a, b, c, d, e);
}
/**
* Array comparator for ReactComponents by mount ordering.
*
* @param {ReactComponent} c1 first component you're comparing
* @param {ReactComponent} c2 second component you're comparing
* @return {number} Return value usable by Array.prototype.sort().
*/
function mountOrderComparator(c1, c2) {
return c1._mountOrder - c2._mountOrder;
}
function runBatchedUpdates(transaction) {
var len = transaction.dirtyComponentsLength;
invariant(
len === dirtyComponents.length,
"Expected flush transaction's stored dirty-components length (%s) to " +
'match dirty-components array length (%s).',
len,
dirtyComponents.length,
);
// Since reconciling a component higher in the owner hierarchy usually (not
// always -- see shouldComponentUpdate()) will reconcile children, reconcile
// them before their children by sorting the array.
dirtyComponents.sort(mountOrderComparator);
// Any updates enqueued while reconciling must be performed after this entire
// batch. Otherwise, if dirtyComponents is [A, B] where A has children B and
// C, B could update twice in a single batch if C's render enqueues an update
// to B (since B would have already updated, we should skip it, and the only
// way we can know to do so is by checking the batch counter).
updateBatchNumber++;
for (var i = 0; i < len; i++) {
// If a component is unmounted before pending changes apply, it will still
// be here, but we assume that it has cleared its _pendingCallbacks and
// that performUpdateIfNecessary is a noop.
var component = dirtyComponents[i];
ReactReconciler.performUpdateIfNecessary(
component,
transaction.reconcileTransaction,
updateBatchNumber,
);
}
}
var flushBatchedUpdates = function() {
// ReactUpdatesFlushTransaction's wrappers will clear the dirtyComponents
// array and perform any updates enqueued by mount-ready handlers (i.e.,
// componentDidUpdate) but we need to check here too in order to catch
// updates enqueued by setState callbacks.
while (dirtyComponents.length) {
var transaction = ReactUpdatesFlushTransaction.getPooled();
transaction.perform(runBatchedUpdates, null, transaction);
ReactUpdatesFlushTransaction.release(transaction);
}
};
/**
* Mark a component as needing a rerender, adding an optional callback to a
* list of functions which will be executed once the rerender occurs.
*/
function enqueueUpdate(component) {
ensureInjected();
// Various parts of our code (such as ReactCompositeComponent's
// _renderValidatedComponent) assume that calls to render aren't nested;
// verify that that's the case. (This is called by each top-level update
// function, like setState, forceUpdate, etc.; creation and
// destruction of top-level components is guarded in ReactMount.)
if (!batchingStrategy.isBatchingUpdates) {
batchingStrategy.batchedUpdates(enqueueUpdate, component);
return;
}
dirtyComponents.push(component);
if (component._updateBatchNumber == null) {
component._updateBatchNumber = updateBatchNumber + 1;
}
}
var ReactUpdatesInjection = {
injectReconcileTransaction: function(ReconcileTransaction) {
invariant(
ReconcileTransaction,
'ReactUpdates: must provide a reconcile transaction class',
);
ReactUpdates.ReactReconcileTransaction = ReconcileTransaction;
},
injectBatchingStrategy: function(_batchingStrategy) {
invariant(
_batchingStrategy,
'ReactUpdates: must provide a batching strategy',
);
invariant(
typeof _batchingStrategy.batchedUpdates === 'function',
'ReactUpdates: must provide a batchedUpdates() function',
);
invariant(
typeof _batchingStrategy.isBatchingUpdates === 'boolean',
'ReactUpdates: must provide an isBatchingUpdates boolean attribute',
);
batchingStrategy = _batchingStrategy;
},
getBatchingStrategy: function() {
return batchingStrategy;
},
};
var ReactUpdates = {
/**
* React references `ReactReconcileTransaction` using this property in order
* to allow dependency injection.
*
* @internal
*/
ReactReconcileTransaction: null,
batchedUpdates: batchedUpdates,
enqueueUpdate: enqueueUpdate,
flushBatchedUpdates: flushBatchedUpdates,
injection: ReactUpdatesInjection,
};
module.exports = ReactUpdates;
@@ -1,241 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Transaction
* @flow
*/
'use strict';
var invariant = require('fbjs/lib/invariant');
var OBSERVED_ERROR = {};
/**
* `Transaction` creates a black box that is able to wrap any method such that
* certain invariants are maintained before and after the method is invoked
* (Even if an exception is thrown while invoking the wrapped method). Whoever
* instantiates a transaction can provide enforcers of the invariants at
* creation time. The `Transaction` class itself will supply one additional
* automatic invariant for you - the invariant that any transaction instance
* should not be run while it is already being run. You would typically create a
* single instance of a `Transaction` for reuse multiple times, that potentially
* is used to wrap several different methods. Wrappers are extremely simple -
* they only require implementing two methods.
*
* <pre>
* wrappers (injected at creation time)
* + +
* | |
* +-----------------|--------|--------------+
* | v | |
* | +---------------+ | |
* | +--| wrapper1 |---|----+ |
* | | +---------------+ v | |
* | | +-------------+ | |
* | | +----| wrapper2 |--------+ |
* | | | +-------------+ | | |
* | | | | | |
* | v v v v | wrapper
* | +---+ +---+ +---------+ +---+ +---+ | invariants
* perform(anyMethod) | | | | | | | | | | | | maintained
* +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | +---+ +---+ +---------+ +---+ +---+ |
* | initialize close |
* +-----------------------------------------+
* </pre>
*
* Use cases:
* - Preserving the input selection ranges before/after reconciliation.
* Restoring selection even in the event of an unexpected error.
* - Deactivating events while rearranging the DOM, preventing blurs/focuses,
* while guaranteeing that afterwards, the event system is reactivated.
* - Flushing a queue of collected DOM mutations to the main UI thread after a
* reconciliation takes place in a worker thread.
* - Invoking any collected `componentDidUpdate` callbacks after rendering new
* content.
* - (Future use case): Wrapping particular flushes of the `ReactWorker` queue
* to preserve the `scrollTop` (an automatic scroll aware DOM).
* - (Future use case): Layout calculations before and after DOM updates.
*
* Transactional plugin API:
* - A module that has an `initialize` method that returns any precomputation.
* - and a `close` method that accepts the precomputation. `close` is invoked
* when the wrapped process is completed, or has failed.
*
* @param {Array<TransactionalWrapper>} transactionWrapper Wrapper modules
* that implement `initialize` and `close`.
* @return {Transaction} Single transaction for reuse in thread.
*
* @class Transaction
*/
var TransactionImpl = {
/**
* Sets up this instance so that it is prepared for collecting metrics. Does
* so such that this setup method may be used on an instance that is already
* initialized, in a way that does not consume additional memory upon reuse.
* That can be useful if you decide to make your subclass of this mixin a
* "PooledClass".
*/
reinitializeTransaction: function(): void {
this.transactionWrappers = this.getTransactionWrappers();
if (this.wrapperInitData) {
this.wrapperInitData.length = 0;
} else {
this.wrapperInitData = [];
}
this._isInTransaction = false;
},
_isInTransaction: false,
/**
* @abstract
* @return {Array<TransactionWrapper>} Array of transaction wrappers.
*/
getTransactionWrappers: null,
isInTransaction: function(): boolean {
return !!this._isInTransaction;
},
/**
* Executes the function within a safety window. Use this for the top level
* methods that result in large amounts of computation/mutations that would
* need to be safety checked. The optional arguments helps prevent the need
* to bind in many cases.
*
* @param {function} method Member of scope to call.
* @param {Object} scope Scope to invoke from.
* @param {Object?=} a Argument to pass to the method.
* @param {Object?=} b Argument to pass to the method.
* @param {Object?=} c Argument to pass to the method.
* @param {Object?=} d Argument to pass to the method.
* @param {Object?=} e Argument to pass to the method.
* @param {Object?=} f Argument to pass to the method.
*
* @return {*} Return value from `method`.
*/
perform: function<
A,
B,
C,
D,
E,
F,
G,
T: (a: A, b: B, c: C, d: D, e: E, f: F) => G
>(method: T, scope: any, a: A, b: B, c: C, d: D, e: E, f: F): G {
invariant(
!this.isInTransaction(),
'Transaction.perform(...): Cannot initialize a transaction when there ' +
'is already an outstanding transaction.',
);
var errorThrown;
var ret;
try {
this._isInTransaction = true;
// Catching errors makes debugging more difficult, so we start with
// errorThrown set to true before setting it to false after calling
// close -- if it's still set to true in the finally block, it means
// one of these calls threw.
errorThrown = true;
this.initializeAll(0);
ret = method.call(scope, a, b, c, d, e, f);
errorThrown = false;
} finally {
try {
if (errorThrown) {
// If `method` throws, prefer to show that stack trace over any thrown
// by invoking `closeAll`.
try {
this.closeAll(0);
} catch (err) {}
} else {
// Since `method` didn't throw, we don't want to silence the exception
// here.
this.closeAll(0);
}
} finally {
this._isInTransaction = false;
}
}
return ret;
},
initializeAll: function(startIndex: number): void {
var transactionWrappers = this.transactionWrappers;
for (var i = startIndex; i < transactionWrappers.length; i++) {
var wrapper = transactionWrappers[i];
try {
// Catching errors makes debugging more difficult, so we start with the
// OBSERVED_ERROR state before overwriting it with the real return value
// of initialize -- if it's still set to OBSERVED_ERROR in the finally
// block, it means wrapper.initialize threw.
this.wrapperInitData[i] = OBSERVED_ERROR;
this.wrapperInitData[i] = wrapper.initialize
? wrapper.initialize.call(this)
: null;
} finally {
if (this.wrapperInitData[i] === OBSERVED_ERROR) {
// The initializer for wrapper i threw an error; initialize the
// remaining wrappers but silence any exceptions from them to ensure
// that the first error is the one to bubble up.
try {
this.initializeAll(i + 1);
} catch (err) {}
}
}
}
},
/**
* Invokes each of `this.transactionWrappers.close[i]` functions, passing into
* them the respective return values of `this.transactionWrappers.init[i]`
* (`close`rs that correspond to initializers that failed will not be
* invoked).
*/
closeAll: function(startIndex: number): void {
invariant(
this.isInTransaction(),
'Transaction.closeAll(): Cannot close transaction when none are open.',
);
var transactionWrappers = this.transactionWrappers;
for (var i = startIndex; i < transactionWrappers.length; i++) {
var wrapper = transactionWrappers[i];
var initData = this.wrapperInitData[i];
var errorThrown;
try {
// Catching errors makes debugging more difficult, so we start with
// errorThrown set to true before setting it to false after calling
// close -- if it's still set to true in the finally block, it means
// wrapper.close threw.
errorThrown = true;
if (initData !== OBSERVED_ERROR && wrapper.close) {
wrapper.close.call(this, initData);
}
errorThrown = false;
} finally {
if (errorThrown) {
// The closer for wrapper i threw an error; close the remaining
// wrappers but silence any exceptions from them to ensure that the
// first error is the one to bubble up.
try {
this.closeAll(i + 1);
} catch (e) {}
}
}
}
this.wrapperInitData.length = 0;
},
};
export type Transaction = typeof TransactionImpl;
module.exports = TransactionImpl;
@@ -1,306 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/
'use strict';
var Transaction;
var INIT_ERRORED = 'initErrored'; // Just a dummy value to check for.
describe('Transaction', () => {
beforeEach(() => {
jest.resetModules();
Transaction = require('Transaction');
});
/**
* We should not invoke closers for inits that failed. We should pass init
* return values to closers when those inits are successful. We should not
* invoke the actual method when any of the initializers fail.
*/
it('should invoke closers with/only-with init returns', () => {
var throwInInit = function() {
throw new Error('close[0] should receive Transaction.OBSERVED_ERROR');
};
var performSideEffect;
var dontPerformThis = function() {
performSideEffect = 'This should never be set to this';
};
/**
* New test Transaction subclass.
*/
var TestTransaction = function() {
this.reinitializeTransaction();
this.firstCloseParam = INIT_ERRORED; // WON'T be set to something else
this.secondCloseParam = INIT_ERRORED; // WILL be set to something else
this.lastCloseParam = INIT_ERRORED; // WON'T be set to something else
};
Object.assign(TestTransaction.prototype, Transaction);
TestTransaction.prototype.getTransactionWrappers = function() {
return [
{
initialize: throwInInit,
close: function(initResult) {
this.firstCloseParam = initResult;
},
},
{
initialize: function() {
return 'asdf';
},
close: function(initResult) {
this.secondCloseParam = initResult;
},
},
{
initialize: throwInInit,
close: function(initResult) {
this.lastCloseParam = initResult;
},
},
];
};
var transaction = new TestTransaction();
expect(function() {
transaction.perform(dontPerformThis);
}).toThrow();
expect(performSideEffect).toBe(undefined);
expect(transaction.firstCloseParam).toBe(INIT_ERRORED);
expect(transaction.secondCloseParam).toBe('asdf');
expect(transaction.lastCloseParam).toBe(INIT_ERRORED);
expect(transaction.isInTransaction()).toBe(false);
});
it('should invoke closers and wrapped method when inits success', () => {
var performSideEffect;
/**
* New test Transaction subclass.
*/
var TestTransaction = function() {
this.reinitializeTransaction();
this.firstCloseParam = INIT_ERRORED; // WILL be set to something else
this.secondCloseParam = INIT_ERRORED; // WILL be set to something else
this.lastCloseParam = INIT_ERRORED; // WILL be set to something else
};
Object.assign(TestTransaction.prototype, Transaction);
TestTransaction.prototype.getTransactionWrappers = function() {
return [
{
initialize: function() {
return 'firstResult';
},
close: function(initResult) {
this.firstCloseParam = initResult;
},
},
{
initialize: function() {
return 'secondResult';
},
close: function(initResult) {
this.secondCloseParam = initResult;
},
},
{
initialize: function() {
return 'thirdResult';
},
close: function(initResult) {
this.lastCloseParam = initResult;
},
},
];
};
var transaction = new TestTransaction();
transaction.perform(function() {
performSideEffect = 'SIDE_EFFECT';
});
expect(performSideEffect).toBe('SIDE_EFFECT');
expect(transaction.firstCloseParam).toBe('firstResult');
expect(transaction.secondCloseParam).toBe('secondResult');
expect(transaction.lastCloseParam).toBe('thirdResult');
expect(transaction.isInTransaction()).toBe(false);
});
/**
* When the operation throws, the transaction should throw, but all of the
* error-free closers should execute gracefully without issue. If a closer
* throws an error, the transaction should prefer to throw the error
* encountered earlier in the operation.
*/
it('should throw when wrapped operation throws', () => {
var performSideEffect;
/**
* New test Transaction subclass.
*/
var TestTransaction = function() {
this.reinitializeTransaction();
this.firstCloseParam = INIT_ERRORED; // WILL be set to something else
this.secondCloseParam = INIT_ERRORED; // WILL be set to something else
this.lastCloseParam = INIT_ERRORED; // WILL be set to something else
};
Object.assign(TestTransaction.prototype, Transaction);
// Now, none of the close/inits throw, but the operation we wrap will throw.
TestTransaction.prototype.getTransactionWrappers = function() {
return [
{
initialize: function() {
return 'firstResult';
},
close: function(initResult) {
this.firstCloseParam = initResult;
},
},
{
initialize: function() {
return 'secondResult';
},
close: function(initResult) {
this.secondCloseParam = initResult;
},
},
{
initialize: function() {
return 'thirdResult';
},
close: function(initResult) {
this.lastCloseParam = initResult;
},
},
{
initialize: function() {
return 'fourthResult';
},
close: function(initResult) {
throw new Error('The transaction should throw a TypeError.');
},
},
];
};
var transaction = new TestTransaction();
expect(
(function() {
var isTypeError = false;
try {
transaction.perform(function() {
throw new TypeError('Thrown in main wrapped operation');
});
} catch (err) {
isTypeError = err instanceof TypeError;
}
return isTypeError;
})(),
).toBe(true);
expect(performSideEffect).toBe(undefined);
expect(transaction.firstCloseParam).toBe('firstResult');
expect(transaction.secondCloseParam).toBe('secondResult');
expect(transaction.lastCloseParam).toBe('thirdResult');
expect(transaction.isInTransaction()).toBe(false);
});
it('should throw errors in transaction close', () => {
var TestTransaction = function() {
this.reinitializeTransaction();
};
Object.assign(TestTransaction.prototype, Transaction);
var exceptionMsg = 'This exception should throw.';
TestTransaction.prototype.getTransactionWrappers = function() {
return [
{
close: function(initResult) {
throw new Error(exceptionMsg);
},
},
];
};
var transaction = new TestTransaction();
expect(function() {
transaction.perform(function() {});
}).toThrowError(exceptionMsg);
expect(transaction.isInTransaction()).toBe(false);
});
it('should allow nesting of transactions', () => {
var performSideEffect;
var nestedPerformSideEffect;
/**
* New test Transaction subclass.
*/
var TestTransaction = function() {
this.reinitializeTransaction();
this.firstCloseParam = INIT_ERRORED; // WILL be set to something else
};
Object.assign(TestTransaction.prototype, Transaction);
TestTransaction.prototype.getTransactionWrappers = function() {
return [
{
initialize: function() {
return 'firstResult';
},
close: function(initResult) {
this.firstCloseParam = initResult;
},
},
{
initialize: function() {
this.nestedTransaction = new NestedTransaction();
},
close: function() {
// Test performing a transaction in another transaction's close()
this.nestedTransaction.perform(function() {
nestedPerformSideEffect = 'NESTED_SIDE_EFFECT';
});
},
},
];
};
var NestedTransaction = function() {
this.reinitializeTransaction();
};
Object.assign(NestedTransaction.prototype, Transaction);
NestedTransaction.prototype.getTransactionWrappers = function() {
return [
{
initialize: function() {
this.hasInitializedNested = true;
},
close: function() {
this.hasClosedNested = true;
},
},
];
};
var transaction = new TestTransaction();
transaction.perform(function() {
performSideEffect = 'SIDE_EFFECT';
});
expect(performSideEffect).toBe('SIDE_EFFECT');
expect(nestedPerformSideEffect).toBe('NESTED_SIDE_EFFECT');
expect(transaction.firstCloseParam).toBe('firstResult');
expect(transaction.isInTransaction()).toBe(false);
expect(transaction.nestedTransaction.hasClosedNested).toBe(true);
expect(transaction.nestedTransaction.hasInitializedNested).toBe(true);
expect(transaction.nestedTransaction.isInTransaction()).toBe(false);
});
});
@@ -1,109 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule flattenStackChildren
* @flow
*/
'use strict';
var KeyEscapeUtils = require('KeyEscapeUtils');
var traverseStackChildren = require('traverseStackChildren');
if (__DEV__) {
var warning = require('fbjs/lib/warning');
}
var ReactComponentTreeHook;
if (
typeof process !== 'undefined' &&
process.env &&
process.env.NODE_ENV === 'test'
) {
// Temporary hack.
// Inline requires don't work well with Jest:
// https://github.com/facebook/react/issues/7240
// Remove the inline requires when we don't need them anymore:
// https://github.com/facebook/react/pull/7178
ReactComponentTreeHook = require('ReactGlobalSharedState')
.ReactComponentTreeHook;
}
/**
* @param {function} traverseContext Context passed through traversal.
* @param {?ReactComponent} child React child component.
* @param {!string} name String name of key path to child.
* @param {number=} selfDebugID Optional debugID of the current internal instance.
*/
function flattenSingleChildIntoContext(
traverseContext: mixed,
child: React$Element<any>,
name: string,
selfDebugID?: number,
): void {
// We found a component instance.
if (traverseContext && typeof traverseContext === 'object') {
const result = traverseContext;
const keyUnique = result[name] === undefined;
if (__DEV__) {
if (!ReactComponentTreeHook) {
ReactComponentTreeHook = require('ReactGlobalSharedState')
.ReactComponentTreeHook;
}
if (!keyUnique) {
warning(
false,
'flattenChildren(...): Encountered two children with the same key, ' +
'`%s`. ' +
'Keys should be unique so that components maintain their identity ' +
'across updates. Non-unique keys may cause children to be ' +
'duplicated and/or omitted — the behavior is unsupported and ' +
'could change in a future version.%s',
KeyEscapeUtils.unescapeInDev(name),
ReactComponentTreeHook.getStackAddendumByID(selfDebugID),
);
}
}
if (keyUnique && child != null) {
result[name] = child;
}
}
}
/**
* Flattens children that are typically specified as `props.children`. Any null
* children will not be included in the resulting object.
* @return {!object} flattened children keyed by name.
*/
function flattenStackChildren(
children: React$Element<any>,
selfDebugID?: number,
): ?{[name: string]: React$Element<any>} {
if (children == null) {
return children;
}
var result = {};
if (__DEV__) {
traverseStackChildren(
children,
(traverseContext, child, name) =>
flattenSingleChildIntoContext(
traverseContext,
child,
name,
selfDebugID,
),
result,
);
} else {
traverseStackChildren(children, flattenSingleChildIntoContext, result);
}
return result;
}
module.exports = flattenStackChildren;
@@ -1,28 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule getHostComponentFromComposite
*/
'use strict';
var ReactNodeTypes = require('ReactNodeTypes');
function getHostComponentFromComposite(inst) {
var type;
while ((type = inst._renderedNodeType) === ReactNodeTypes.COMPOSITE) {
inst = inst._renderedComponent;
}
if (type === ReactNodeTypes.HOST) {
return inst._renderedComponent;
} else if (type === ReactNodeTypes.EMPTY) {
return null;
}
}
module.exports = getHostComponentFromComposite;
@@ -1,156 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule instantiateReactComponent
*/
'use strict';
var ReactCompositeComponent = require('ReactCompositeComponent');
var ReactEmptyComponent = require('ReactEmptyComponent');
var ReactHostComponent = require('ReactHostComponent');
var invariant = require('fbjs/lib/invariant');
if (__DEV__) {
var warning = require('fbjs/lib/warning');
}
var nextDebugID = 1;
// To avoid a cyclic dependency, we create the final class in this module
var ReactCompositeComponentWrapper = function(element) {
this.construct(element);
};
function getDeclarationErrorAddendum(owner) {
if (owner) {
var name = owner.getName();
if (name) {
return '\n\nCheck the render method of `' + name + '`.';
}
}
return '';
}
/**
* Check if the type reference is a known internal type. I.e. not a user
* provided composite type.
*
* @param {function} type
* @return {boolean} Returns true if this is a valid internal type.
*/
function isInternalComponentType(type) {
return (
typeof type === 'function' &&
typeof type.prototype !== 'undefined' &&
typeof type.prototype.mountComponent === 'function' &&
typeof type.prototype.receiveComponent === 'function'
);
}
/**
* Given a ReactNode, create an instance that will actually be mounted.
*
* @param {ReactNode} node
* @param {boolean} shouldHaveDebugID
* @return {object} A new instance of the element's constructor.
* @protected
*/
function instantiateReactComponent(node, shouldHaveDebugID) {
var instance;
if (node === null || node === false) {
instance = ReactEmptyComponent.create(instantiateReactComponent);
} else if (typeof node === 'object') {
var element = node;
var type = element.type;
if (typeof type !== 'function' && typeof type !== 'string') {
var info = '';
if (__DEV__) {
if (
type === undefined ||
(typeof type === 'object' &&
type !== null &&
Object.keys(type).length === 0)
) {
info +=
' You likely forgot to export your component from the file ' +
"it's defined in.";
}
}
info += getDeclarationErrorAddendum(element._owner);
invariant(
false,
'Element type is invalid: expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: %s.%s',
type == null ? type : typeof type,
info,
);
}
// Special case string values
if (typeof element.type === 'string') {
instance = ReactHostComponent.createInternalComponent(element);
} else if (isInternalComponentType(element.type)) {
// This is temporarily available for custom components that are not string
// representations. I.e. ART. Once those are updated to use the string
// representation, we can drop this code path.
instance = new element.type(element);
// We renamed this. Allow the old name for compat. :(
if (!instance.getHostNode) {
instance.getHostNode = instance.getNativeNode;
}
} else {
instance = new ReactCompositeComponentWrapper(element);
}
} else if (typeof node === 'string' || typeof node === 'number') {
instance = ReactHostComponent.createInstanceForText(node);
} else {
invariant(false, 'Encountered invalid React node of type %s', typeof node);
}
if (__DEV__) {
warning(
typeof instance.mountComponent === 'function' &&
typeof instance.receiveComponent === 'function' &&
typeof instance.getHostNode === 'function' &&
typeof instance.unmountComponent === 'function',
'Only React Components can be mounted.',
);
}
// These two fields are used by the DOM and ART diffing algorithms
// respectively. Instead of using expandos on components, we should be
// storing the state needed by the diffing algorithms elsewhere.
instance._mountIndex = 0;
instance._mountImage = null;
if (__DEV__) {
instance._debugID = shouldHaveDebugID ? nextDebugID++ : 0;
}
// Internal instances should fully constructed at this point, so they should
// not get any new fields added to them at this point.
if (__DEV__) {
if (Object.preventExtensions) {
Object.preventExtensions(instance);
}
}
return instance;
}
Object.assign(
ReactCompositeComponentWrapper.prototype,
ReactCompositeComponent,
{
_instantiateReactComponent: instantiateReactComponent,
},
);
module.exports = instantiateReactComponent;
@@ -1,198 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule traverseStackChildren
*/
'use strict';
var invariant = require('fbjs/lib/invariant');
var KeyEscapeUtils = require('KeyEscapeUtils');
var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec.
// The Symbol used to tag the ReactElement type. If there is no native Symbol
// nor polyfill, then a plain number is used for performance.
var REACT_ELEMENT_TYPE =
(typeof Symbol === 'function' && Symbol.for && Symbol.for('react.element')) ||
0xeac7;
if (__DEV__) {
var warning = require('fbjs/lib/warning');
var {
getCurrentStackAddendum,
} = require('ReactGlobalSharedState').ReactComponentTreeHook;
}
var SEPARATOR = '.';
var SUBSEPARATOR = ':';
/**
* This is inlined from ReactElement since this file is shared between
* isomorphic and renderers. We could extract this to a
*
*/
/**
* TODO: Test that a single child and an array with one item have the same key
* pattern.
*/
var didWarnAboutMaps = false;
/**
* Generate a key string that identifies a component within a set.
*
* @param {*} component A component that could contain a manual key.
* @param {number} index Index that is used if a manual key is not provided.
* @return {string}
*/
function getComponentKey(component, index) {
// Do some typechecking here since we call this blindly. We want to ensure
// that we don't block potential future ES APIs.
if (component && typeof component === 'object' && component.key != null) {
// Explicit key
return KeyEscapeUtils.escape(component.key);
}
// Implicit key determined by the index in the set
return index.toString(36);
}
/**
* @param {?*} children Children tree container.
* @param {!string} nameSoFar Name of the key path so far.
* @param {!function} callback Callback to invoke with each child found.
* @param {?*} traverseContext Used to pass information throughout the traversal
* process.
* @return {!number} The number of children in this subtree.
*/
function traverseStackChildrenImpl(
children,
nameSoFar,
callback,
traverseContext,
) {
var type = typeof children;
if (type === 'undefined' || type === 'boolean') {
// All of the above are perceived as null.
children = null;
}
if (
children === null ||
type === 'string' ||
type === 'number' ||
// The following is inlined from ReactElement. This means we can optimize
// some checks. React Fiber also inlines this logic for similar purposes.
(type === 'object' && children.$$typeof === REACT_ELEMENT_TYPE)
) {
callback(
traverseContext,
children,
// If it's the only child, treat the name as if it was wrapped in an array
// so that it's consistent if the number of children grows.
nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,
);
return 1;
}
var child;
var nextName;
var subtreeCount = 0; // Count of children found in the current subtree.
var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
if (Array.isArray(children)) {
for (var i = 0; i < children.length; i++) {
child = children[i];
nextName = nextNamePrefix + getComponentKey(child, i);
subtreeCount += traverseStackChildrenImpl(
child,
nextName,
callback,
traverseContext,
);
}
} else {
var iteratorFn =
(ITERATOR_SYMBOL && children[ITERATOR_SYMBOL]) ||
children[FAUX_ITERATOR_SYMBOL];
if (typeof iteratorFn === 'function') {
if (__DEV__) {
// Warn about using Maps as children
if (iteratorFn === children.entries) {
warning(
didWarnAboutMaps,
'Using Maps as children is unsupported and will likely yield ' +
'unexpected results. Convert it to a sequence/iterable of keyed ' +
'ReactElements instead.%s',
getCurrentStackAddendum(),
);
didWarnAboutMaps = true;
}
}
var iterator = iteratorFn.call(children);
var step;
var ii = 0;
while (!(step = iterator.next()).done) {
child = step.value;
nextName = nextNamePrefix + getComponentKey(child, ii++);
subtreeCount += traverseStackChildrenImpl(
child,
nextName,
callback,
traverseContext,
);
}
} else if (type === 'object') {
var addendum = '';
if (__DEV__) {
addendum =
' If you meant to render a collection of children, use an array ' +
'instead.' +
getCurrentStackAddendum();
}
var childrenString = '' + children;
invariant(
false,
'Objects are not valid as a React child (found: %s).%s',
childrenString === '[object Object]'
? 'object with keys {' + Object.keys(children).join(', ') + '}'
: childrenString,
addendum,
);
}
}
return subtreeCount;
}
/**
* Traverses children that are typically specified as `props.children`, but
* might also be specified through attributes:
*
* - `traverseStackChildren(this.props.children, ...)`
* - `traverseStackChildren(this.props.leftPanelChildren, ...)`
*
* The `traverseContext` is an optional argument that is passed through the
* entire traversal. It can be used to store accumulations or anything else that
* the callback might find relevant.
*
* @param {?*} children Children tree object.
* @param {!function} callback To invoke upon traversing each child.
* @param {?*} traverseContext Context for traversal.
* @return {!number} The number of children in this subtree.
*/
function traverseStackChildren(children, callback, traverseContext) {
if (children == null) {
return 0;
}
return traverseStackChildrenImpl(children, '', callback, traverseContext);
}
module.exports = traverseStackChildren;
+29
View File
@@ -0,0 +1,29 @@
/**
* Copyright 2016-present, 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 ReactInstanceType
* @flow
*/
'use strict';
// TODO: delete this file.
// It was only necessary for Stack.
import type {ReactElement} from 'ReactElementType';
export type DebugID = number;
export type ReactInstance = {
mountComponent: any,
unmountComponent: any,
receiveComponent: any,
getName: () => string,
getPublicInstance: any,
_currentElement: ReactElement,
};