mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
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:
+35
-4
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, {
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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');
|
||||
@@ -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
@@ -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
@@ -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
@@ -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');
|
||||
|
||||
@@ -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;
|
||||
@@ -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=">" ' +
|
||||
ROOT_ATTRIBUTE_NAME +
|
||||
'=""' +
|
||||
(ReactDOMFeatureFlags.useFiber
|
||||
? ''
|
||||
: ' ' + ID_ATTRIBUTE_NAME + '="[^"]*"') +
|
||||
(ReactDOMFeatureFlags.useFiber
|
||||
? ''
|
||||
: ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
|
||||
'/>',
|
||||
'<img data-attr=">" ' + 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;
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
};
|
||||
Reference in New Issue
Block a user