mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Merge branch '15.1.0-dev' into 15-stable
This commit is contained in:
@@ -1,3 +1,27 @@
|
||||
## 15.1.0 (May 20, 2016)
|
||||
|
||||
### React
|
||||
- Ensure we're using the latest `object-assign`, which has protection against a non-spec-compliant native `Object.assign`. ([@zpao](https://github.com/zpao) in [#6681](https://github.com/facebook/react/pull/6681))
|
||||
- Add a new warning to communicate that `props` objects passed to `createElement` must be plain objects. ([@richardscarrott](https://github.com/richardscarrott) in [#6134](https://github.com/facebook/react/pull/6134))
|
||||
- Fix a batching bug resulting in some lifecycle methods incorrectly being called multiple times. ([@spicyj](https://github.com/spicyj) in [#6650](https://github.com/facebook/react/pull/6650))
|
||||
|
||||
### React DOM
|
||||
- Fix regression in custom elements support. ([@jscissr](https://github.com/jscissr) in [#6570](https://github.com/facebook/react/pull/6570))
|
||||
- Stop incorrectly warning about using `onScroll` event handler with server rendering. ([@Aweary](https://github.com/Aweary) in [#6678](https://github.com/facebook/react/pull/6678))
|
||||
- Fix grammar in the controlled input warning. ([@jakeboone02](https://github.com/jakeboone02) in [#6657](https://github.com/facebook/react/pull/6657))
|
||||
- Fix issue preventing `<object>` nodes from being able to read `<param>` nodes in IE. ([@syranide](https://github.com/syranide) in [#6691](https://github.com/facebook/react/pull/6691))
|
||||
- Fix issue resulting in crash when using experimental error boundaries with server rendering. ([@jimfb](https://github.com/jimfb) in [#6694](https://github.com/facebook/react/pull/6694))
|
||||
- Add additional information to the controlled input warning. ([@borisyankov](https://github.com/borisyankov) in [#6341](https://github.com/facebook/react/pull/6341))
|
||||
|
||||
### React Perf Add-on
|
||||
- Completely rewritten to collect data more accurately and to be easier to maintain. ([@gaearon](https://github.com/gaearon) in [#6647](https://github.com/facebook/react/pull/6647), [#6046](https://github.com/facebook/react/pull/6046))
|
||||
|
||||
### React Native Renderer
|
||||
- Remove some special cases for platform specific branching. ([@sebmarkbage](https://github.com/sebmarkbage) in [#6660](https://github.com/facebook/react/pull/6660))
|
||||
- Remove use of `merge` utility. ([@sebmarkbage](https://github.com/sebmarkbage) in [#6634](https://github.com/facebook/react/pull/6634))
|
||||
- Renamed some modules to better indicate usage ([@javache](https://github.com/javache) in [#6643](https://github.com/facebook/react/pull/6643))
|
||||
|
||||
|
||||
## 15.0.2 (April 29, 2016)
|
||||
|
||||
### React
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
"dependencies": {
|
||||
"babel-preset-es2015": "^6.6.0",
|
||||
"babel-preset-react": "^6.5.0",
|
||||
"babelify": "^7.2.0",
|
||||
"browserify": "^11.0.1",
|
||||
"react": "15.0.1",
|
||||
"react-dom": "15.0.1",
|
||||
"watchify": "^3.4.0"
|
||||
"babelify": "^7.3.0",
|
||||
"browserify": "^13.0.0",
|
||||
"react": "^15.0.2",
|
||||
"react-dom": "^15.0.2",
|
||||
"watchify": "^3.7.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "browserify ./index.js -t babelify -o bundle.js",
|
||||
|
||||
@@ -10,7 +10,7 @@ module.exports = {
|
||||
archive: './build/react-' + version + '.zip',
|
||||
},
|
||||
files: [
|
||||
{cwd: './build/starter', src: ['**'], dest: 'react-' + version + '/'},
|
||||
{cwd: './build/starter', src: ['**'], dot: true, dest: 'react-' + version + '/'},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ function writeTempConfig(callback) {
|
||||
function run(done, configPath) {
|
||||
grunt.log.writeln('running jest (this may take a while)');
|
||||
|
||||
var args = ['--harmony', path.join('node_modules', 'jest-cli', 'bin', 'jest')];
|
||||
var args = ['--harmony', path.join('node_modules', 'jest-cli', 'bin', 'jest'), '--runInBand'];
|
||||
if (configPath) {
|
||||
args.push('--config', configPath);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ var addons = {
|
||||
docs: 'two-way-binding-helpers',
|
||||
},
|
||||
Perf: {
|
||||
module: 'ReactDefaultPerf',
|
||||
module: 'ReactPerf',
|
||||
name: 'perf',
|
||||
docs: 'perf',
|
||||
},
|
||||
|
||||
@@ -39,12 +39,7 @@ var whiteListNames = [
|
||||
'deepFreezeAndThrowOnMutationInDev',
|
||||
'flattenStyle',
|
||||
'InitializeJavaScriptAppEngine',
|
||||
'InteractionManager',
|
||||
'JSTimersExecution',
|
||||
'merge',
|
||||
'Platform',
|
||||
'RCTEventEmitter',
|
||||
'RCTLog',
|
||||
'TextInputState',
|
||||
'UIManager',
|
||||
'View',
|
||||
|
||||
Generated
+547
-542
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-build",
|
||||
"private": true,
|
||||
"version": "15.0.2",
|
||||
"version": "15.1.0-alpha.1",
|
||||
"devDependencies": {
|
||||
"async": "^1.5.0",
|
||||
"babel-cli": "^6.6.5",
|
||||
@@ -48,9 +48,9 @@
|
||||
"gulp-babel": "^6.0.0",
|
||||
"gulp-flatten": "^0.2.0",
|
||||
"gzip-js": "~0.3.2",
|
||||
"jest-cli": "^0.9.0",
|
||||
"jest-cli": "^12.0.2",
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.0.1",
|
||||
"object-assign": "^4.1.0",
|
||||
"platform": "^1.1.0",
|
||||
"run-sequence": "^1.1.4",
|
||||
"through2": "^2.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-addons-template",
|
||||
"version": "15.0.2",
|
||||
"version": "15.1.0-alpha.1",
|
||||
"main": "index.js",
|
||||
"repository": "facebook/react",
|
||||
"keywords": [
|
||||
@@ -10,6 +10,6 @@
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {},
|
||||
"peerDependencies": {
|
||||
"react": "^15.0.2"
|
||||
"react": "^15.1.0-alpha.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-dom",
|
||||
"version": "15.0.2",
|
||||
"version": "15.1.0-alpha.1",
|
||||
"description": "React package for working with the DOM.",
|
||||
"main": "index.js",
|
||||
"repository": "facebook/react",
|
||||
@@ -14,6 +14,6 @@
|
||||
"homepage": "https://facebook.github.io/react/",
|
||||
"dependencies": {},
|
||||
"peerDependencies": {
|
||||
"react": "^15.0.2"
|
||||
"react": "^15.1.0-alpha.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native-renderer",
|
||||
"version": "15.0.2",
|
||||
"version": "15.1.0-alpha.1",
|
||||
"description": "React package for use inside react-native.",
|
||||
"main": "index.js",
|
||||
"repository": "facebook/react",
|
||||
@@ -14,6 +14,6 @@
|
||||
},
|
||||
"homepage": "https://facebook.github.io/react-native/",
|
||||
"dependencies": {
|
||||
"react": "^15.0.2"
|
||||
"react": "^15.1.0-alpha.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react",
|
||||
"description": "React is a JavaScript library for building user interfaces.",
|
||||
"version": "15.0.2",
|
||||
"version": "15.1.0-alpha.1",
|
||||
"keywords": [
|
||||
"react"
|
||||
],
|
||||
@@ -25,7 +25,7 @@
|
||||
"dependencies": {
|
||||
"fbjs": "^0.8.0",
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.0.1"
|
||||
"object-assign": "^4.1.0"
|
||||
},
|
||||
"browserify": {
|
||||
"transform": [
|
||||
|
||||
+1
-1
@@ -11,4 +11,4 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = '15.0.2';
|
||||
module.exports = '15.1.0-alpha.1';
|
||||
|
||||
@@ -36,6 +36,8 @@ var shallowCompare = require('shallowCompare');
|
||||
* complex data structures this mixin may have false-negatives for deeper
|
||||
* differences. Only mixin to components which have simple props and state, or
|
||||
* use `forceUpdate()` when you know deep data structures have changed.
|
||||
*
|
||||
* See https://facebook.github.io/react/docs/pure-render-mixin.html
|
||||
*/
|
||||
var ReactComponentWithPureRenderMixin = {
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
|
||||
@@ -31,8 +31,11 @@ var numericPropertyRegex = /^\d+$/;
|
||||
var warnedAboutNumeric = false;
|
||||
|
||||
var ReactFragment = {
|
||||
// Wrap a keyed object in an opaque proxy that warns you if you access any
|
||||
// of its properties.
|
||||
/**
|
||||
* Wrap a keyed object in an opaque proxy that warns you if you access any
|
||||
* of its properties.
|
||||
* See https://facebook.github.io/react/docs/create-fragment.html
|
||||
*/
|
||||
create: function(object) {
|
||||
if (typeof object !== 'object' || !object || Array.isArray(object)) {
|
||||
warning(
|
||||
|
||||
@@ -34,7 +34,7 @@ React.addons = {
|
||||
};
|
||||
|
||||
if (__DEV__) {
|
||||
React.addons.Perf = require('ReactDefaultPerf');
|
||||
React.addons.Perf = require('ReactPerf');
|
||||
React.addons.TestUtils = require('ReactTestUtils');
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ var ReactStateSetters = require('ReactStateSetters');
|
||||
|
||||
/**
|
||||
* A simple mixin around ReactLink.forState().
|
||||
* See https://facebook.github.io/react/docs/two-way-binding-helpers.html
|
||||
*/
|
||||
var LinkedStateMixin = {
|
||||
/**
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
var React = require('React');
|
||||
|
||||
/**
|
||||
* Deprecated: An an easy way to express two-way binding with React.
|
||||
* See https://facebook.github.io/react/docs/two-way-binding-helpers.html
|
||||
*
|
||||
* @param {*} value current value of the link
|
||||
* @param {function} requestChange callback to request a change
|
||||
*/
|
||||
|
||||
@@ -16,6 +16,7 @@ var shallowEqual = require('shallowEqual');
|
||||
/**
|
||||
* Does a shallow comparison for props and state.
|
||||
* See ReactComponentWithPureRenderMixin
|
||||
* See also https://facebook.github.io/react/docs/shallow-compare.html
|
||||
*/
|
||||
function shallowCompare(instance, nextProps, nextState) {
|
||||
return (
|
||||
|
||||
@@ -41,6 +41,11 @@ function createTransitionTimeoutPropValidator(transitionType) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* An easy way to perform CSS transitions and animations when a React component
|
||||
* enters or leaves the DOM.
|
||||
* See https://facebook.github.io/react/docs/animation.html#high-level-api-reactcsstransitiongroup
|
||||
*/
|
||||
var ReactCSSTransitionGroup = React.createClass({
|
||||
displayName: 'ReactCSSTransitionGroup',
|
||||
|
||||
|
||||
@@ -16,6 +16,11 @@ var ReactTransitionChildMapping = require('ReactTransitionChildMapping');
|
||||
|
||||
var emptyFunction = require('emptyFunction');
|
||||
|
||||
/**
|
||||
* A basis for animatins. When children are declaratively added or removed,
|
||||
* special lifecycle hooks are called.
|
||||
* See https://facebook.github.io/react/docs/animation.html#low-level-api-reacttransitiongroup
|
||||
*/
|
||||
var ReactTransitionGroup = React.createClass({
|
||||
displayName: 'ReactTransitionGroup',
|
||||
|
||||
|
||||
@@ -66,6 +66,10 @@ function invariantArrayCase(value, spec, command) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a updated shallow copy of an object without mutating the original.
|
||||
* See https://facebook.github.io/react/docs/update.html for details.
|
||||
*/
|
||||
function update(value, spec) {
|
||||
invariant(
|
||||
typeof spec === 'object',
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
|
||||
var React;
|
||||
var ReactDOM;
|
||||
var ReactDOMServer;
|
||||
|
||||
describe('ReactErrorBoundaries', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
ReactDOM = require('ReactDOM');
|
||||
ReactDOMServer = require('ReactDOMServer');
|
||||
React = require('React');
|
||||
});
|
||||
|
||||
@@ -50,11 +52,46 @@ describe('ReactErrorBoundaries', function() {
|
||||
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var container = document.createElement('div');
|
||||
EventPluginHub.putListener = jest.genMockFn();
|
||||
EventPluginHub.putListener = jest.fn();
|
||||
ReactDOM.render(<Boundary />, container);
|
||||
expect(EventPluginHub.putListener).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('renders an error state (ssr)', function() {
|
||||
class Angry extends React.Component {
|
||||
render() {
|
||||
throw new Error('Please, do not render me.');
|
||||
}
|
||||
}
|
||||
|
||||
class Boundary extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {error: false};
|
||||
}
|
||||
render() {
|
||||
if (!this.state.error) {
|
||||
return (<div><button onClick={this.onClick}>ClickMe</button><Angry /></div>);
|
||||
} else {
|
||||
return (<div>Happy Birthday!</div>);
|
||||
}
|
||||
}
|
||||
onClick() {
|
||||
/* do nothing */
|
||||
}
|
||||
unstable_handleError() {
|
||||
this.setState({error: true});
|
||||
}
|
||||
}
|
||||
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var container = document.createElement('div');
|
||||
EventPluginHub.putListener = jest.fn();
|
||||
container.innerHTML = ReactDOMServer.renderToString(<Boundary />);
|
||||
expect(container.firstChild.innerHTML).toBe('Happy Birthday!');
|
||||
expect(EventPluginHub.putListener).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('will catch exceptions in componentWillUnmount', function() {
|
||||
class ErrorBoundary extends React.Component {
|
||||
constructor() {
|
||||
@@ -120,7 +157,7 @@ describe('ReactErrorBoundaries', function() {
|
||||
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var container = document.createElement('div');
|
||||
EventPluginHub.putListener = jest.genMockFn();
|
||||
EventPluginHub.putListener = jest.fn();
|
||||
ReactDOM.render(<Boundary />, container);
|
||||
expect(EventPluginHub.putListener).toBeCalled();
|
||||
});
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
/**
|
||||
* 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 ReactDebugInstanceMap
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var warning = require('warning');
|
||||
|
||||
function checkValidInstance(internalInstance) {
|
||||
if (!internalInstance) {
|
||||
warning(
|
||||
false,
|
||||
'There is an internal error in the React developer tools integration. ' +
|
||||
'Instead of an internal instance, received %s. ' +
|
||||
'Please report this as a bug in React.',
|
||||
internalInstance
|
||||
);
|
||||
return false;
|
||||
}
|
||||
var isValid = typeof internalInstance.mountComponent === 'function';
|
||||
warning(
|
||||
isValid,
|
||||
'There is an internal error in the React developer tools integration. ' +
|
||||
'Instead of an internal instance, received an object with the following ' +
|
||||
'keys: %s. Please report this as a bug in React.',
|
||||
Object.keys(internalInstance).join(', ')
|
||||
);
|
||||
return isValid;
|
||||
}
|
||||
|
||||
var idCounter = 1;
|
||||
var instancesByIDs = {};
|
||||
var instancesToIDs;
|
||||
|
||||
function getIDForInstance(internalInstance) {
|
||||
if (!instancesToIDs) {
|
||||
instancesToIDs = new WeakMap();
|
||||
}
|
||||
if (instancesToIDs.has(internalInstance)) {
|
||||
return instancesToIDs.get(internalInstance);
|
||||
} else {
|
||||
var instanceID = (idCounter++).toString();
|
||||
instancesToIDs.set(internalInstance, instanceID);
|
||||
return instanceID;
|
||||
}
|
||||
}
|
||||
|
||||
function getInstanceByID(instanceID) {
|
||||
return instancesByIDs[instanceID] || null;
|
||||
}
|
||||
|
||||
function isRegisteredInstance(internalInstance) {
|
||||
var instanceID = getIDForInstance(internalInstance);
|
||||
if (instanceID) {
|
||||
return instancesByIDs.hasOwnProperty(instanceID);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function registerInstance(internalInstance) {
|
||||
var instanceID = getIDForInstance(internalInstance);
|
||||
if (instanceID) {
|
||||
instancesByIDs[instanceID] = internalInstance;
|
||||
}
|
||||
}
|
||||
|
||||
function unregisterInstance(internalInstance) {
|
||||
var instanceID = getIDForInstance(internalInstance);
|
||||
if (instanceID) {
|
||||
delete instancesByIDs[instanceID];
|
||||
}
|
||||
}
|
||||
|
||||
var ReactDebugInstanceMap = {
|
||||
getIDForInstance(internalInstance) {
|
||||
if (!checkValidInstance(internalInstance)) {
|
||||
return null;
|
||||
}
|
||||
return getIDForInstance(internalInstance);
|
||||
},
|
||||
getInstanceByID(instanceID) {
|
||||
return getInstanceByID(instanceID);
|
||||
},
|
||||
isRegisteredInstance(internalInstance) {
|
||||
if (!checkValidInstance(internalInstance)) {
|
||||
return false;
|
||||
}
|
||||
return isRegisteredInstance(internalInstance);
|
||||
},
|
||||
registerInstance(internalInstance) {
|
||||
if (!checkValidInstance(internalInstance)) {
|
||||
return;
|
||||
}
|
||||
warning(
|
||||
!isRegisteredInstance(internalInstance),
|
||||
'There is an internal error in the React developer tools integration. ' +
|
||||
'A registered instance should not be registered again. ' +
|
||||
'Please report this as a bug in React.'
|
||||
);
|
||||
registerInstance(internalInstance);
|
||||
},
|
||||
unregisterInstance(internalInstance) {
|
||||
if (!checkValidInstance(internalInstance)) {
|
||||
return;
|
||||
}
|
||||
warning(
|
||||
isRegisteredInstance(internalInstance),
|
||||
'There is an internal error in the React developer tools integration. ' +
|
||||
'An unregistered instance should not be unregistered again. ' +
|
||||
'Please report this as a bug in React.'
|
||||
);
|
||||
unregisterInstance(internalInstance);
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = ReactDebugInstanceMap;
|
||||
@@ -11,7 +11,9 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var ReactInvalidSetStateWarningDevTool = require('ReactInvalidSetStateWarningDevTool');
|
||||
var ExecutionEnvironment = require('ExecutionEnvironment');
|
||||
|
||||
var performanceNow = require('performanceNow');
|
||||
var warning = require('warning');
|
||||
|
||||
var eventHandlers = [];
|
||||
@@ -37,6 +39,70 @@ function emitEvent(handlerFunctionName, arg1, arg2, arg3, arg4, arg5) {
|
||||
}
|
||||
}
|
||||
|
||||
var isProfiling = false;
|
||||
var flushHistory = [];
|
||||
var currentFlushNesting = 0;
|
||||
var currentFlushMeasurements = null;
|
||||
var currentFlushStartTime = null;
|
||||
var currentTimerDebugID = null;
|
||||
var currentTimerStartTime = null;
|
||||
var currentTimerType = null;
|
||||
|
||||
function clearHistory() {
|
||||
ReactComponentTreeDevtool.purgeUnmountedComponents();
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
}
|
||||
|
||||
function getTreeSnapshot(registeredIDs) {
|
||||
return registeredIDs.reduce((tree, id) => {
|
||||
var ownerID = ReactComponentTreeDevtool.getOwnerID(id);
|
||||
var parentID = ReactComponentTreeDevtool.getParentID(id);
|
||||
tree[id] = {
|
||||
displayName: ReactComponentTreeDevtool.getDisplayName(id),
|
||||
text: ReactComponentTreeDevtool.getText(id),
|
||||
updateCount: ReactComponentTreeDevtool.getUpdateCount(id),
|
||||
childIDs: ReactComponentTreeDevtool.getChildIDs(id),
|
||||
// Text nodes don't have owners but this is close enough.
|
||||
ownerID: ownerID || ReactComponentTreeDevtool.getOwnerID(parentID),
|
||||
parentID,
|
||||
};
|
||||
return tree;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function resetMeasurements() {
|
||||
if (__DEV__) {
|
||||
var previousStartTime = currentFlushStartTime;
|
||||
var previousMeasurements = currentFlushMeasurements || [];
|
||||
var previousOperations = ReactNativeOperationHistoryDevtool.getHistory();
|
||||
|
||||
if (!isProfiling || currentFlushNesting === 0) {
|
||||
currentFlushStartTime = null;
|
||||
currentFlushMeasurements = null;
|
||||
clearHistory();
|
||||
return;
|
||||
}
|
||||
|
||||
if (previousMeasurements.length || previousOperations.length) {
|
||||
var registeredIDs = ReactComponentTreeDevtool.getRegisteredIDs();
|
||||
flushHistory.push({
|
||||
duration: performanceNow() - previousStartTime,
|
||||
measurements: previousMeasurements || [],
|
||||
operations: previousOperations || [],
|
||||
treeSnapshot: getTreeSnapshot(registeredIDs),
|
||||
});
|
||||
}
|
||||
|
||||
clearHistory();
|
||||
currentFlushStartTime = performanceNow();
|
||||
currentFlushMeasurements = [];
|
||||
}
|
||||
}
|
||||
|
||||
function checkDebugID(debugID) {
|
||||
warning(debugID, 'ReactDebugTool: debugID may not be empty.');
|
||||
}
|
||||
|
||||
var ReactDebugTool = {
|
||||
addDevtool(devtool) {
|
||||
eventHandlers.push(devtool);
|
||||
@@ -49,29 +115,157 @@ var ReactDebugTool = {
|
||||
}
|
||||
}
|
||||
},
|
||||
beginProfiling() {
|
||||
if (__DEV__) {
|
||||
if (isProfiling) {
|
||||
return;
|
||||
}
|
||||
|
||||
isProfiling = true;
|
||||
flushHistory.length = 0;
|
||||
resetMeasurements();
|
||||
}
|
||||
},
|
||||
endProfiling() {
|
||||
if (__DEV__) {
|
||||
if (!isProfiling) {
|
||||
return;
|
||||
}
|
||||
|
||||
isProfiling = false;
|
||||
resetMeasurements();
|
||||
}
|
||||
},
|
||||
getFlushHistory() {
|
||||
if (__DEV__) {
|
||||
return flushHistory;
|
||||
}
|
||||
},
|
||||
onBeginFlush() {
|
||||
if (__DEV__) {
|
||||
currentFlushNesting++;
|
||||
resetMeasurements();
|
||||
}
|
||||
emitEvent('onBeginFlush');
|
||||
},
|
||||
onEndFlush() {
|
||||
if (__DEV__) {
|
||||
resetMeasurements();
|
||||
currentFlushNesting--;
|
||||
}
|
||||
emitEvent('onEndFlush');
|
||||
},
|
||||
onBeginLifeCycleTimer(debugID, timerType) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onBeginLifeCycleTimer', debugID, timerType);
|
||||
if (__DEV__) {
|
||||
if (isProfiling && currentFlushNesting > 0) {
|
||||
warning(
|
||||
!currentTimerType,
|
||||
'There is an internal error in the React performance measurement code. ' +
|
||||
'Did not expect %s timer to start while %s timer is still in ' +
|
||||
'progress for %s instance.',
|
||||
timerType,
|
||||
currentTimerType || 'no',
|
||||
(debugID === currentTimerDebugID) ? 'the same' : 'another'
|
||||
);
|
||||
currentTimerStartTime = performanceNow();
|
||||
currentTimerDebugID = debugID;
|
||||
currentTimerType = timerType;
|
||||
}
|
||||
}
|
||||
},
|
||||
onEndLifeCycleTimer(debugID, timerType) {
|
||||
checkDebugID(debugID);
|
||||
if (__DEV__) {
|
||||
if (isProfiling && currentFlushNesting > 0) {
|
||||
warning(
|
||||
currentTimerType === timerType,
|
||||
'There is an internal error in the React performance measurement code. ' +
|
||||
'We did not expect %s timer to stop while %s timer is still in ' +
|
||||
'progress for %s instance. Please report this as a bug in React.',
|
||||
timerType,
|
||||
currentTimerType || 'no',
|
||||
(debugID === currentTimerDebugID) ? 'the same' : 'another'
|
||||
);
|
||||
currentFlushMeasurements.push({
|
||||
timerType,
|
||||
instanceID: debugID,
|
||||
duration: performanceNow() - currentTimerStartTime,
|
||||
});
|
||||
currentTimerStartTime = null;
|
||||
currentTimerDebugID = null;
|
||||
currentTimerType = null;
|
||||
}
|
||||
}
|
||||
emitEvent('onEndLifeCycleTimer', debugID, timerType);
|
||||
},
|
||||
onBeginReconcilerTimer(debugID, timerType) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onBeginReconcilerTimer', debugID, timerType);
|
||||
},
|
||||
onEndReconcilerTimer(debugID, timerType) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onEndReconcilerTimer', debugID, timerType);
|
||||
},
|
||||
onBeginProcessingChildContext() {
|
||||
emitEvent('onBeginProcessingChildContext');
|
||||
},
|
||||
onEndProcessingChildContext() {
|
||||
emitEvent('onEndProcessingChildContext');
|
||||
},
|
||||
onNativeOperation(debugID, type, payload) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onNativeOperation', debugID, type, payload);
|
||||
},
|
||||
onSetState() {
|
||||
emitEvent('onSetState');
|
||||
},
|
||||
onMountRootComponent(internalInstance) {
|
||||
emitEvent('onMountRootComponent', internalInstance);
|
||||
onSetDisplayName(debugID, displayName) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onSetDisplayName', debugID, displayName);
|
||||
},
|
||||
onMountComponent(internalInstance) {
|
||||
emitEvent('onMountComponent', internalInstance);
|
||||
onSetChildren(debugID, childDebugIDs) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onSetChildren', debugID, childDebugIDs);
|
||||
},
|
||||
onUpdateComponent(internalInstance) {
|
||||
emitEvent('onUpdateComponent', internalInstance);
|
||||
onSetOwner(debugID, ownerDebugID) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onSetOwner', debugID, ownerDebugID);
|
||||
},
|
||||
onUnmountComponent(internalInstance) {
|
||||
emitEvent('onUnmountComponent', internalInstance);
|
||||
onSetText(debugID, text) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onSetText', debugID, text);
|
||||
},
|
||||
onMountRootComponent(debugID) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onMountRootComponent', debugID);
|
||||
},
|
||||
onMountComponent(debugID) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onMountComponent', debugID);
|
||||
},
|
||||
onUpdateComponent(debugID) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onUpdateComponent', debugID);
|
||||
},
|
||||
onUnmountComponent(debugID) {
|
||||
checkDebugID(debugID);
|
||||
emitEvent('onUnmountComponent', debugID);
|
||||
},
|
||||
};
|
||||
|
||||
ReactDebugTool.addDevtool(ReactInvalidSetStateWarningDevTool);
|
||||
if (__DEV__) {
|
||||
var ReactInvalidSetStateWarningDevTool = require('ReactInvalidSetStateWarningDevTool');
|
||||
var ReactNativeOperationHistoryDevtool = require('ReactNativeOperationHistoryDevtool');
|
||||
var ReactComponentTreeDevtool = require('ReactComponentTreeDevtool');
|
||||
ReactDebugTool.addDevtool(ReactInvalidSetStateWarningDevTool);
|
||||
ReactDebugTool.addDevtool(ReactComponentTreeDevtool);
|
||||
ReactDebugTool.addDevtool(ReactNativeOperationHistoryDevtool);
|
||||
var url = (ExecutionEnvironment.canUseDOM && window.location.href) || '';
|
||||
if ((/[?&]react_perf\b/).test(url)) {
|
||||
ReactDebugTool.beginProfiling();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ReactDebugTool;
|
||||
|
||||
@@ -0,0 +1,371 @@
|
||||
/**
|
||||
* 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 ReactPerf
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var ReactDebugTool = require('ReactDebugTool');
|
||||
var warning = require('warning');
|
||||
|
||||
function roundFloat(val, base = 2) {
|
||||
var n = Math.pow(10, base);
|
||||
return Math.floor(val * n) / n;
|
||||
}
|
||||
|
||||
function getFlushHistory() {
|
||||
return ReactDebugTool.getFlushHistory();
|
||||
}
|
||||
|
||||
function getExclusive(flushHistory = getFlushHistory()) {
|
||||
var aggregatedStats = {};
|
||||
var affectedIDs = {};
|
||||
|
||||
function updateAggregatedStats(treeSnapshot, instanceID, timerType, applyUpdate) {
|
||||
var {displayName} = treeSnapshot[instanceID];
|
||||
var key = displayName;
|
||||
var stats = aggregatedStats[key];
|
||||
if (!stats) {
|
||||
affectedIDs[key] = {};
|
||||
stats = aggregatedStats[key] = {
|
||||
key,
|
||||
instanceCount: 0,
|
||||
counts: {},
|
||||
durations: {},
|
||||
totalDuration: 0,
|
||||
};
|
||||
}
|
||||
if (!stats.durations[timerType]) {
|
||||
stats.durations[timerType] = 0;
|
||||
}
|
||||
if (!stats.counts[timerType]) {
|
||||
stats.counts[timerType] = 0;
|
||||
}
|
||||
affectedIDs[key][instanceID] = true;
|
||||
applyUpdate(stats);
|
||||
}
|
||||
|
||||
flushHistory.forEach(flush => {
|
||||
var {measurements, treeSnapshot} = flush;
|
||||
measurements.forEach(measurement => {
|
||||
var {duration, instanceID, timerType} = measurement;
|
||||
updateAggregatedStats(treeSnapshot, instanceID, timerType, stats => {
|
||||
stats.totalDuration += duration;
|
||||
stats.durations[timerType] += duration;
|
||||
stats.counts[timerType]++;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return Object.keys(aggregatedStats)
|
||||
.map(key => ({
|
||||
...aggregatedStats[key],
|
||||
instanceCount: Object.keys(affectedIDs[key]).length,
|
||||
}))
|
||||
.sort((a, b) =>
|
||||
b.totalDuration - a.totalDuration
|
||||
);
|
||||
}
|
||||
|
||||
function getInclusive(flushHistory = getFlushHistory()) {
|
||||
var aggregatedStats = {};
|
||||
var affectedIDs = {};
|
||||
|
||||
function updateAggregatedStats(treeSnapshot, instanceID, applyUpdate) {
|
||||
var {displayName, ownerID} = treeSnapshot[instanceID];
|
||||
var owner = treeSnapshot[ownerID];
|
||||
var key = (owner ? owner.displayName + ' > ' : '') + displayName;
|
||||
var stats = aggregatedStats[key];
|
||||
if (!stats) {
|
||||
affectedIDs[key] = {};
|
||||
stats = aggregatedStats[key] = {
|
||||
key,
|
||||
instanceCount: 0,
|
||||
inclusiveRenderDuration: 0,
|
||||
renderCount: 0,
|
||||
};
|
||||
}
|
||||
affectedIDs[key][instanceID] = true;
|
||||
applyUpdate(stats);
|
||||
}
|
||||
|
||||
var isCompositeByID = {};
|
||||
flushHistory.forEach(flush => {
|
||||
var {measurements} = flush;
|
||||
measurements.forEach(measurement => {
|
||||
var {instanceID, timerType} = measurement;
|
||||
if (timerType !== 'render') {
|
||||
return;
|
||||
}
|
||||
isCompositeByID[instanceID] = true;
|
||||
});
|
||||
});
|
||||
|
||||
flushHistory.forEach(flush => {
|
||||
var {measurements, treeSnapshot} = flush;
|
||||
measurements.forEach(measurement => {
|
||||
var {duration, instanceID, timerType} = measurement;
|
||||
if (timerType !== 'render') {
|
||||
return;
|
||||
}
|
||||
updateAggregatedStats(treeSnapshot, instanceID, stats => {
|
||||
stats.renderCount++;
|
||||
});
|
||||
var nextParentID = instanceID;
|
||||
while (nextParentID) {
|
||||
// As we traverse parents, only count inclusive time towards composites.
|
||||
// We know something is a composite if its render() was called.
|
||||
if (isCompositeByID[nextParentID]) {
|
||||
updateAggregatedStats(treeSnapshot, nextParentID, stats => {
|
||||
stats.inclusiveRenderDuration += duration;
|
||||
});
|
||||
}
|
||||
nextParentID = treeSnapshot[nextParentID].parentID;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return Object.keys(aggregatedStats)
|
||||
.map(key => ({
|
||||
...aggregatedStats[key],
|
||||
instanceCount: Object.keys(affectedIDs[key]).length,
|
||||
}))
|
||||
.sort((a, b) =>
|
||||
b.inclusiveRenderDuration - a.inclusiveRenderDuration
|
||||
);
|
||||
}
|
||||
|
||||
function getWasted(flushHistory = getFlushHistory()) {
|
||||
var aggregatedStats = {};
|
||||
var affectedIDs = {};
|
||||
|
||||
function updateAggregatedStats(treeSnapshot, instanceID, applyUpdate) {
|
||||
var {displayName, ownerID} = treeSnapshot[instanceID];
|
||||
var owner = treeSnapshot[ownerID];
|
||||
var key = (owner ? owner.displayName + ' > ' : '') + displayName;
|
||||
var stats = aggregatedStats[key];
|
||||
if (!stats) {
|
||||
affectedIDs[key] = {};
|
||||
stats = aggregatedStats[key] = {
|
||||
key,
|
||||
instanceCount: 0,
|
||||
inclusiveRenderDuration: 0,
|
||||
renderCount: 0,
|
||||
};
|
||||
}
|
||||
affectedIDs[key][instanceID] = true;
|
||||
applyUpdate(stats);
|
||||
}
|
||||
|
||||
flushHistory.forEach(flush => {
|
||||
var {measurements, treeSnapshot, operations} = flush;
|
||||
var isDefinitelyNotWastedByID = {};
|
||||
|
||||
// Find native components associated with an operation in this batch.
|
||||
// Mark all components in their parent tree as definitely not wasted.
|
||||
operations.forEach(operation => {
|
||||
var {instanceID} = operation;
|
||||
var nextParentID = instanceID;
|
||||
while (nextParentID) {
|
||||
isDefinitelyNotWastedByID[nextParentID] = true;
|
||||
nextParentID = treeSnapshot[nextParentID].parentID;
|
||||
}
|
||||
});
|
||||
|
||||
// Find composite components that rendered in this batch.
|
||||
// These are potential candidates for being wasted renders.
|
||||
var renderedCompositeIDs = {};
|
||||
measurements.forEach(measurement => {
|
||||
var {instanceID, timerType} = measurement;
|
||||
if (timerType !== 'render') {
|
||||
return;
|
||||
}
|
||||
renderedCompositeIDs[instanceID] = true;
|
||||
});
|
||||
|
||||
measurements.forEach(measurement => {
|
||||
var {duration, instanceID, timerType} = measurement;
|
||||
if (timerType !== 'render') {
|
||||
return;
|
||||
}
|
||||
|
||||
// If there was a DOM update below this component, or it has just been
|
||||
// mounted, its render() is not considered wasted.
|
||||
var { updateCount } = treeSnapshot[instanceID];
|
||||
if (isDefinitelyNotWastedByID[instanceID] || updateCount === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We consider this render() wasted.
|
||||
updateAggregatedStats(treeSnapshot, instanceID, stats => {
|
||||
stats.renderCount++;
|
||||
});
|
||||
|
||||
var nextParentID = instanceID;
|
||||
while (nextParentID) {
|
||||
// Any parents rendered during this batch are considered wasted
|
||||
// unless we previously marked them as dirty.
|
||||
var isWasted =
|
||||
renderedCompositeIDs[nextParentID] &&
|
||||
!isDefinitelyNotWastedByID[nextParentID];
|
||||
if (isWasted) {
|
||||
updateAggregatedStats(treeSnapshot, nextParentID, stats => {
|
||||
stats.inclusiveRenderDuration += duration;
|
||||
});
|
||||
}
|
||||
nextParentID = treeSnapshot[nextParentID].parentID;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return Object.keys(aggregatedStats)
|
||||
.map(key => ({
|
||||
...aggregatedStats[key],
|
||||
instanceCount: Object.keys(affectedIDs[key]).length,
|
||||
}))
|
||||
.sort((a, b) =>
|
||||
b.inclusiveRenderDuration - a.inclusiveRenderDuration
|
||||
);
|
||||
}
|
||||
|
||||
function getOperations(flushHistory = getFlushHistory()) {
|
||||
var stats = [];
|
||||
flushHistory.forEach((flush, flushIndex) => {
|
||||
var {operations, treeSnapshot} = flush;
|
||||
operations.forEach(operation => {
|
||||
var {instanceID, type, payload} = operation;
|
||||
var {displayName, ownerID} = treeSnapshot[instanceID];
|
||||
var owner = treeSnapshot[ownerID];
|
||||
var key = (owner ? owner.displayName + ' > ' : '') + displayName;
|
||||
|
||||
stats.push({
|
||||
flushIndex,
|
||||
instanceID,
|
||||
key,
|
||||
type,
|
||||
ownerID,
|
||||
payload,
|
||||
});
|
||||
});
|
||||
});
|
||||
return stats;
|
||||
}
|
||||
|
||||
function printExclusive(flushHistory) {
|
||||
var stats = getExclusive(flushHistory);
|
||||
var table = stats.map(item => {
|
||||
var {key, instanceCount, totalDuration} = item;
|
||||
var renderCount = item.counts.render || 0;
|
||||
var renderDuration = item.durations.render || 0;
|
||||
return {
|
||||
'Component': key,
|
||||
'Total time (ms)': roundFloat(totalDuration),
|
||||
'Instance count': instanceCount,
|
||||
'Total render time (ms)': roundFloat(renderDuration),
|
||||
'Average render time (ms)': renderCount ?
|
||||
roundFloat(renderDuration / renderCount) :
|
||||
undefined,
|
||||
'Render count': renderCount,
|
||||
'Total lifecycle time (ms)': roundFloat(totalDuration - renderDuration),
|
||||
};
|
||||
});
|
||||
console.table(table);
|
||||
}
|
||||
|
||||
function printInclusive(flushHistory) {
|
||||
var stats = getInclusive(flushHistory);
|
||||
var table = stats.map(item => {
|
||||
var {key, instanceCount, inclusiveRenderDuration, renderCount} = item;
|
||||
return {
|
||||
'Owner > Component': key,
|
||||
'Inclusive render time (ms)': roundFloat(inclusiveRenderDuration),
|
||||
'Instance count': instanceCount,
|
||||
'Render count': renderCount,
|
||||
};
|
||||
});
|
||||
console.table(table);
|
||||
}
|
||||
|
||||
function printWasted(flushHistory) {
|
||||
var stats = getWasted(flushHistory);
|
||||
var table = stats.map(item => {
|
||||
var {key, instanceCount, inclusiveRenderDuration, renderCount} = item;
|
||||
return {
|
||||
'Owner > Component': key,
|
||||
'Inclusive wasted time (ms)': roundFloat(inclusiveRenderDuration),
|
||||
'Instance count': instanceCount,
|
||||
'Render count': renderCount,
|
||||
};
|
||||
});
|
||||
console.table(table);
|
||||
}
|
||||
|
||||
function printOperations(flushHistory) {
|
||||
var stats = getOperations(flushHistory);
|
||||
var table = stats.map(stat => ({
|
||||
'Owner > Node': stat.key,
|
||||
'Operation': stat.type,
|
||||
'Payload': typeof stat.payload === 'object' ?
|
||||
JSON.stringify(stat.payload) :
|
||||
stat.payload,
|
||||
'Flush index': stat.flushIndex,
|
||||
'Owner Component ID': stat.ownerID,
|
||||
'DOM Component ID': stat.instanceID,
|
||||
}));
|
||||
console.table(table);
|
||||
}
|
||||
|
||||
var warnedAboutPrintDOM = false;
|
||||
function printDOM(measurements) {
|
||||
warning(
|
||||
warnedAboutPrintDOM,
|
||||
'`ReactPerf.printDOM(...)` is deprecated. Use ' +
|
||||
'`ReactPerf.printOperations(...)` instead.'
|
||||
);
|
||||
warnedAboutPrintDOM = true;
|
||||
return printOperations(measurements);
|
||||
}
|
||||
|
||||
var warnedAboutGetMeasurementsSummaryMap = false;
|
||||
function getMeasurementsSummaryMap(measurements) {
|
||||
warning(
|
||||
warnedAboutGetMeasurementsSummaryMap,
|
||||
'`ReactPerf.getMeasurementsSummaryMap(...)` is deprecated. Use ' +
|
||||
'`ReactPerf.getWasted(...)` instead.'
|
||||
);
|
||||
warnedAboutGetMeasurementsSummaryMap = true;
|
||||
return getWasted(measurements);
|
||||
}
|
||||
|
||||
function start() {
|
||||
ReactDebugTool.beginProfiling();
|
||||
}
|
||||
|
||||
function stop() {
|
||||
ReactDebugTool.endProfiling();
|
||||
}
|
||||
|
||||
var ReactPerfAnalysis = {
|
||||
getLastMeasurements: getFlushHistory,
|
||||
getExclusive,
|
||||
getInclusive,
|
||||
getWasted,
|
||||
getOperations,
|
||||
printExclusive,
|
||||
printInclusive,
|
||||
printWasted,
|
||||
printOperations,
|
||||
start,
|
||||
stop,
|
||||
// Deprecated:
|
||||
printDOM,
|
||||
getMeasurementsSummaryMap,
|
||||
};
|
||||
|
||||
module.exports = ReactPerfAnalysis;
|
||||
@@ -1,173 +0,0 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
describe('ReactDebugInstanceMap', function() {
|
||||
var React;
|
||||
var ReactDebugInstanceMap;
|
||||
var ReactDOM;
|
||||
|
||||
beforeEach(function() {
|
||||
jest.resetModuleRegistry();
|
||||
React = require('React');
|
||||
ReactDebugInstanceMap = require('ReactDebugInstanceMap');
|
||||
ReactDOM = require('ReactDOM');
|
||||
});
|
||||
|
||||
function createStubInstance() {
|
||||
return { mountComponent: () => {} };
|
||||
}
|
||||
|
||||
it('should register and unregister instances', function() {
|
||||
var inst1 = createStubInstance();
|
||||
var inst2 = createStubInstance();
|
||||
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(false);
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(false);
|
||||
|
||||
ReactDebugInstanceMap.registerInstance(inst1);
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(true);
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(false);
|
||||
|
||||
ReactDebugInstanceMap.registerInstance(inst2);
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(true);
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(true);
|
||||
|
||||
ReactDebugInstanceMap.unregisterInstance(inst2);
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(true);
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(false);
|
||||
|
||||
ReactDebugInstanceMap.unregisterInstance(inst1);
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(false);
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(false);
|
||||
});
|
||||
|
||||
it('should assign stable IDs', function() {
|
||||
var inst1 = createStubInstance();
|
||||
var inst2 = createStubInstance();
|
||||
|
||||
var inst1ID = ReactDebugInstanceMap.getIDForInstance(inst1);
|
||||
var inst2ID = ReactDebugInstanceMap.getIDForInstance(inst2);
|
||||
expect(typeof inst1ID).toBe('string');
|
||||
expect(typeof inst2ID).toBe('string');
|
||||
expect(inst1ID).not.toBe(inst2ID);
|
||||
|
||||
ReactDebugInstanceMap.registerInstance(inst1);
|
||||
ReactDebugInstanceMap.registerInstance(inst2);
|
||||
expect(ReactDebugInstanceMap.getIDForInstance(inst1)).toBe(inst1ID);
|
||||
expect(ReactDebugInstanceMap.getIDForInstance(inst2)).toBe(inst2ID);
|
||||
|
||||
ReactDebugInstanceMap.unregisterInstance(inst1);
|
||||
ReactDebugInstanceMap.unregisterInstance(inst2);
|
||||
expect(ReactDebugInstanceMap.getIDForInstance(inst1)).toBe(inst1ID);
|
||||
expect(ReactDebugInstanceMap.getIDForInstance(inst2)).toBe(inst2ID);
|
||||
});
|
||||
|
||||
it('should retrieve registered instance by its ID', function() {
|
||||
var inst1 = createStubInstance();
|
||||
var inst2 = createStubInstance();
|
||||
|
||||
var inst1ID = ReactDebugInstanceMap.getIDForInstance(inst1);
|
||||
var inst2ID = ReactDebugInstanceMap.getIDForInstance(inst2);
|
||||
expect(ReactDebugInstanceMap.getInstanceByID(inst1ID)).toBe(null);
|
||||
expect(ReactDebugInstanceMap.getInstanceByID(inst2ID)).toBe(null);
|
||||
|
||||
ReactDebugInstanceMap.registerInstance(inst1);
|
||||
ReactDebugInstanceMap.registerInstance(inst2);
|
||||
expect(ReactDebugInstanceMap.getInstanceByID(inst1ID)).toBe(inst1);
|
||||
expect(ReactDebugInstanceMap.getInstanceByID(inst2ID)).toBe(inst2);
|
||||
|
||||
ReactDebugInstanceMap.unregisterInstance(inst1);
|
||||
ReactDebugInstanceMap.unregisterInstance(inst2);
|
||||
expect(ReactDebugInstanceMap.getInstanceByID(inst1ID)).toBe(null);
|
||||
expect(ReactDebugInstanceMap.getInstanceByID(inst2ID)).toBe(null);
|
||||
});
|
||||
|
||||
it('should warn when registering an instance twice', function() {
|
||||
spyOn(console, 'error');
|
||||
|
||||
var inst = createStubInstance();
|
||||
ReactDebugInstanceMap.registerInstance(inst);
|
||||
expect(console.error.argsForCall.length).toBe(0);
|
||||
|
||||
ReactDebugInstanceMap.registerInstance(inst);
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'There is an internal error in the React developer tools integration. ' +
|
||||
'A registered instance should not be registered again. ' +
|
||||
'Please report this as a bug in React.'
|
||||
);
|
||||
|
||||
ReactDebugInstanceMap.unregisterInstance(inst);
|
||||
ReactDebugInstanceMap.registerInstance(inst);
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should warn when unregistering an instance twice', function() {
|
||||
spyOn(console, 'error');
|
||||
var inst = createStubInstance();
|
||||
|
||||
ReactDebugInstanceMap.unregisterInstance(inst);
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'There is an internal error in the React developer tools integration. ' +
|
||||
'An unregistered instance should not be unregistered again. ' +
|
||||
'Please report this as a bug in React.'
|
||||
);
|
||||
|
||||
ReactDebugInstanceMap.registerInstance(inst);
|
||||
ReactDebugInstanceMap.unregisterInstance(inst);
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
|
||||
ReactDebugInstanceMap.unregisterInstance(inst);
|
||||
expect(console.error.argsForCall.length).toBe(2);
|
||||
expect(console.error.argsForCall[1][0]).toContain(
|
||||
'There is an internal error in the React developer tools integration. ' +
|
||||
'An unregistered instance should not be unregistered again. ' +
|
||||
'Please report this as a bug in React.'
|
||||
);
|
||||
});
|
||||
|
||||
it('should warn about anything than is not an internal instance', function() {
|
||||
class Foo extends React.Component {
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
spyOn(console, 'error');
|
||||
var warningCount = 0;
|
||||
var div = document.createElement('div');
|
||||
var publicInst = ReactDOM.render(<Foo />, div);
|
||||
|
||||
[false, null, undefined, {}, div, publicInst].forEach(falsyValue => {
|
||||
ReactDebugInstanceMap.registerInstance(falsyValue);
|
||||
warningCount++;
|
||||
expect(ReactDebugInstanceMap.getIDForInstance(falsyValue)).toBe(null);
|
||||
warningCount++;
|
||||
expect(ReactDebugInstanceMap.isRegisteredInstance(falsyValue)).toBe(false);
|
||||
warningCount++;
|
||||
ReactDebugInstanceMap.unregisterInstance(falsyValue);
|
||||
warningCount++;
|
||||
});
|
||||
|
||||
expect(console.error.argsForCall.length).toBe(warningCount);
|
||||
for (var i = 0; i < warningCount.length; i++) {
|
||||
// Ideally we could check for the more detailed error message here
|
||||
// but it depends on the input type and is meant for internal bugs
|
||||
// anyway so I don't think it's worth complicating the test with it.
|
||||
expect(console.error.argsForCall[i][0]).toContain(
|
||||
'There is an internal error in the React developer tools integration.'
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
+67
-80
@@ -11,13 +11,11 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
describe('ReactDefaultPerf', function() {
|
||||
describe('ReactPerf', function() {
|
||||
var React;
|
||||
var ReactDOM;
|
||||
var ReactDOMFeatureFlags;
|
||||
var ReactDefaultPerf;
|
||||
var ReactPerf;
|
||||
var ReactTestUtils;
|
||||
var ReactDefaultPerfAnalysis;
|
||||
|
||||
var App;
|
||||
var Box;
|
||||
@@ -36,10 +34,8 @@ describe('ReactDefaultPerf', function() {
|
||||
|
||||
React = require('React');
|
||||
ReactDOM = require('ReactDOM');
|
||||
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
|
||||
ReactDefaultPerf = require('ReactDefaultPerf');
|
||||
ReactPerf = require('ReactPerf');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
ReactDefaultPerfAnalysis = require('ReactDefaultPerfAnalysis');
|
||||
|
||||
App = React.createClass({
|
||||
render: function() {
|
||||
@@ -68,10 +64,17 @@ describe('ReactDefaultPerf', function() {
|
||||
});
|
||||
|
||||
function measure(fn) {
|
||||
ReactDefaultPerf.start();
|
||||
ReactPerf.start();
|
||||
fn();
|
||||
ReactDefaultPerf.stop();
|
||||
return ReactDefaultPerf.getLastMeasurements().__unstable_this_format_will_change;
|
||||
ReactPerf.stop();
|
||||
|
||||
// Make sure none of the methods crash.
|
||||
ReactPerf.getWasted();
|
||||
ReactPerf.getInclusive();
|
||||
ReactPerf.getExclusive();
|
||||
ReactPerf.getOperations();
|
||||
|
||||
return ReactPerf.getLastMeasurements();
|
||||
}
|
||||
|
||||
it('should count no-op update as waste', function() {
|
||||
@@ -81,20 +84,18 @@ describe('ReactDefaultPerf', function() {
|
||||
ReactDOM.render(<App />, container);
|
||||
});
|
||||
|
||||
var summary = ReactDefaultPerf.getWasted(measurements);
|
||||
expect(summary.length).toBe(2);
|
||||
|
||||
/*eslint-disable dot-notation */
|
||||
|
||||
expect(summary[0]['Owner > component']).toBe('<root> > App');
|
||||
expect(summary[0]['Wasted time (ms)']).not.toBe(0);
|
||||
expect(summary[0]['Instances']).toBe(1);
|
||||
|
||||
expect(summary[1]['Owner > component']).toBe('App > Box');
|
||||
expect(summary[1]['Wasted time (ms)']).not.toBe(0);
|
||||
expect(summary[1]['Instances']).toBe(2);
|
||||
|
||||
/*eslint-enable dot-notation */
|
||||
var summary = ReactPerf.getWasted(measurements);
|
||||
expect(summary).toEqual([{
|
||||
key: 'App',
|
||||
instanceCount: 1,
|
||||
inclusiveRenderDuration: 3,
|
||||
renderCount: 1,
|
||||
}, {
|
||||
key: 'App > Box',
|
||||
instanceCount: 2,
|
||||
inclusiveRenderDuration: 2,
|
||||
renderCount: 2,
|
||||
}]);
|
||||
});
|
||||
|
||||
it('should count no-op update in child as waste', function() {
|
||||
@@ -107,21 +108,18 @@ describe('ReactDefaultPerf', function() {
|
||||
ReactDOM.render(<App flipSecond={true} />, container);
|
||||
});
|
||||
|
||||
var summary = ReactDefaultPerf.getWasted(measurements);
|
||||
expect(summary.length).toBe(1);
|
||||
|
||||
/*eslint-disable dot-notation */
|
||||
|
||||
expect(summary[0]['Owner > component']).toBe('App > Box');
|
||||
expect(summary[0]['Wasted time (ms)']).not.toBe(0);
|
||||
expect(summary[0]['Instances']).toBe(1);
|
||||
|
||||
/*eslint-enable dot-notation */
|
||||
var summary = ReactPerf.getWasted(measurements);
|
||||
expect(summary).toEqual([{
|
||||
key: 'App > Box',
|
||||
instanceCount: 1,
|
||||
inclusiveRenderDuration: 1,
|
||||
renderCount: 1,
|
||||
}]);
|
||||
});
|
||||
|
||||
function expectNoWaste(fn) {
|
||||
var measurements = measure(fn);
|
||||
var summary = ReactDefaultPerf.getWasted(measurements);
|
||||
var summary = ReactPerf.getWasted(measurements);
|
||||
expect(summary).toEqual([]);
|
||||
}
|
||||
|
||||
@@ -217,83 +215,72 @@ describe('ReactDefaultPerf', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('putListener should not be instrumented', function() {
|
||||
it('should not count replacing null with a native as waste', function() {
|
||||
var element = null;
|
||||
function Foo() {
|
||||
return element;
|
||||
}
|
||||
var container = document.createElement('div');
|
||||
ReactDOM.render(<Div onClick={function() {}}>hey</Div>, container);
|
||||
var measurements = measure(() => {
|
||||
ReactDOM.render(<Div onClick={function() {}}>hey</Div>, container);
|
||||
});
|
||||
|
||||
var summary = ReactDefaultPerfAnalysis.getDOMSummary(measurements);
|
||||
expect(summary).toEqual([]);
|
||||
});
|
||||
|
||||
it('deleteListener should not be instrumented', function() {
|
||||
var container = document.createElement('div');
|
||||
ReactDOM.render(<Div onClick={function() {}}>hey</Div>, container);
|
||||
var measurements = measure(() => {
|
||||
ReactDOM.render(<Div>hey</Div>, container);
|
||||
});
|
||||
|
||||
var summary = ReactDefaultPerfAnalysis.getDOMSummary(measurements);
|
||||
expect(summary).toEqual([]);
|
||||
});
|
||||
|
||||
it('should not fail on input change events', function() {
|
||||
var container = document.createElement('div');
|
||||
var onChange = () => {};
|
||||
var input = ReactDOM.render(
|
||||
<input checked={true} onChange={onChange} />,
|
||||
container
|
||||
);
|
||||
ReactDOM.render(<Foo />, container);
|
||||
expectNoWaste(() => {
|
||||
ReactTestUtils.Simulate.change(input);
|
||||
element = <div />;
|
||||
ReactDOM.render(<Foo />, container);
|
||||
});
|
||||
});
|
||||
|
||||
it('should print a table after calling printOperations', function() {
|
||||
it('should not count replacing a native with null as waste', function() {
|
||||
var element = <div />;
|
||||
function Foo() {
|
||||
return element;
|
||||
}
|
||||
var container = document.createElement('div');
|
||||
ReactDOM.render(<Foo />, container);
|
||||
expectNoWaste(() => {
|
||||
element = null;
|
||||
ReactDOM.render(<Foo />, container);
|
||||
});
|
||||
});
|
||||
|
||||
it('should include stats for components unmounted during measurement', function() {
|
||||
var container = document.createElement('div');
|
||||
var measurements = measure(() => {
|
||||
ReactDOM.render(<Div>hey</Div>, container);
|
||||
ReactDOM.render(<Div><Div key="a" /></Div>, container);
|
||||
ReactDOM.render(<Div><Div key="b" /></Div>, container);
|
||||
});
|
||||
spyOn(console, 'table');
|
||||
ReactDefaultPerf.printOperations(measurements);
|
||||
expect(console.table.calls.length).toBe(1);
|
||||
expect(console.table.argsForCall[0][0]).toEqual([{
|
||||
'data-reactid': '',
|
||||
type: 'set innerHTML',
|
||||
args: ReactDOMFeatureFlags.useCreateElement ?
|
||||
'{"node":"<not serializable>","children":[],"html":null,"text":null}' :
|
||||
'"<div data-reactroot=\\"\\" data-reactid=\\"1\\">hey</div>"',
|
||||
expect(ReactPerf.getExclusive(measurements)).toEqual([{
|
||||
key: 'Div',
|
||||
instanceCount: 3,
|
||||
counts: { ctor: 3, render: 4 },
|
||||
durations: { ctor: 3, render: 4 },
|
||||
totalDuration: 7,
|
||||
}]);
|
||||
});
|
||||
|
||||
it('warns once when using getMeasurementsSummaryMap', function() {
|
||||
var measurements = measure(() => {});
|
||||
spyOn(console, 'error');
|
||||
ReactDefaultPerf.getMeasurementsSummaryMap(measurements);
|
||||
ReactPerf.getMeasurementsSummaryMap(measurements);
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'`ReactPerf.getMeasurementsSummaryMap(...)` is deprecated. Use ' +
|
||||
'`ReactPerf.getWasted(...)` instead.'
|
||||
);
|
||||
|
||||
ReactDefaultPerf.getMeasurementsSummaryMap(measurements);
|
||||
ReactPerf.getMeasurementsSummaryMap(measurements);
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('warns once when using printDOM', function() {
|
||||
var measurements = measure(() => {});
|
||||
spyOn(console, 'error');
|
||||
ReactDefaultPerf.printDOM(measurements);
|
||||
ReactPerf.printDOM(measurements);
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'`ReactPerf.printDOM(...)` is deprecated. Use ' +
|
||||
'`ReactPerf.printOperations(...)` instead.'
|
||||
);
|
||||
|
||||
ReactDefaultPerf.printDOM(measurements);
|
||||
ReactPerf.printDOM(measurements);
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -55,6 +55,8 @@ function forEachSingleChild(bookKeeping, child, name) {
|
||||
/**
|
||||
* Iterates through children that are typically specified as `props.children`.
|
||||
*
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.children.foreach
|
||||
*
|
||||
* The provided forEachFunc(child, index) will be called for each
|
||||
* leaf child.
|
||||
*
|
||||
@@ -146,7 +148,9 @@ function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
|
||||
/**
|
||||
* Maps children that are typically specified as `props.children`.
|
||||
*
|
||||
* The provided mapFunction(child, index) will be called for each
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.children.map
|
||||
*
|
||||
* The provided mapFunction(child, key, index) will be called for each
|
||||
* leaf child.
|
||||
*
|
||||
* @param {?*} children Children tree container.
|
||||
@@ -173,6 +177,8 @@ function forEachSingleChildDummy(traverseContext, child, name) {
|
||||
* Count the number of children that are typically specified as
|
||||
* `props.children`.
|
||||
*
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.children.count
|
||||
*
|
||||
* @param {?*} children Children tree container.
|
||||
* @return {number} The number of children.
|
||||
*/
|
||||
@@ -184,6 +190,8 @@ function countChildren(children, context) {
|
||||
/**
|
||||
* Flatten a children object (typically specified as `props.children`) and
|
||||
* return an array with appropriately re-keyed children.
|
||||
*
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.children.toarray
|
||||
*/
|
||||
function toArray(children) {
|
||||
var result = [];
|
||||
|
||||
@@ -16,10 +16,13 @@ var invariant = require('invariant');
|
||||
|
||||
/**
|
||||
* Returns the first child in a collection of children and verifies that there
|
||||
* is only one child in the collection. The current implementation of this
|
||||
* function assumes that a single child gets passed without a wrapper, but the
|
||||
* purpose of this helper function is to abstract away the particular structure
|
||||
* of children.
|
||||
* is only one child in the collection.
|
||||
*
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.children.only
|
||||
*
|
||||
* The current implementation of this function assumes that a single child gets
|
||||
* passed without a wrapper, but the purpose of this helper function is to
|
||||
* abstract away the particular structure of children.
|
||||
*
|
||||
* @param {?object} children Child collection structure.
|
||||
* @return {ReactElement} The first and only `ReactElement` contained in the
|
||||
|
||||
@@ -740,6 +740,7 @@ var ReactClass = {
|
||||
|
||||
/**
|
||||
* Creates a composite component class given a class specification.
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.createclass
|
||||
*
|
||||
* @param {object} spec Class specification (which must define `render`).
|
||||
* @return {function} Component constructor function.
|
||||
|
||||
@@ -20,9 +20,9 @@ describe('autobinding', function() {
|
||||
|
||||
it('Holds reference to instance', function() {
|
||||
|
||||
var mouseDidEnter = jest.genMockFn();
|
||||
var mouseDidLeave = jest.genMockFn();
|
||||
var mouseDidClick = jest.genMockFn();
|
||||
var mouseDidEnter = jest.fn();
|
||||
var mouseDidLeave = jest.fn();
|
||||
var mouseDidClick = jest.fn();
|
||||
|
||||
var TestBindComponent = React.createClass({
|
||||
getInitialState: function() {
|
||||
@@ -95,7 +95,7 @@ describe('autobinding', function() {
|
||||
});
|
||||
|
||||
it('works with mixins', function() {
|
||||
var mouseDidClick = jest.genMockFn();
|
||||
var mouseDidClick = jest.fn();
|
||||
|
||||
var TestMixin = {
|
||||
onClick: mouseDidClick,
|
||||
|
||||
@@ -20,9 +20,9 @@ describe('autobind optout', function() {
|
||||
|
||||
it('should work with manual binding', function() {
|
||||
|
||||
var mouseDidEnter = jest.genMockFn();
|
||||
var mouseDidLeave = jest.genMockFn();
|
||||
var mouseDidClick = jest.genMockFn();
|
||||
var mouseDidEnter = jest.fn();
|
||||
var mouseDidLeave = jest.fn();
|
||||
var mouseDidClick = jest.fn();
|
||||
|
||||
var TestBindComponent = React.createClass({
|
||||
autobind: false,
|
||||
@@ -138,7 +138,7 @@ describe('autobind optout', function() {
|
||||
});
|
||||
|
||||
it('works with mixins that have not opted out of autobinding', function() {
|
||||
var mouseDidClick = jest.genMockFn();
|
||||
var mouseDidClick = jest.fn();
|
||||
|
||||
var TestMixin = {
|
||||
onClick: mouseDidClick,
|
||||
@@ -164,7 +164,7 @@ describe('autobind optout', function() {
|
||||
});
|
||||
|
||||
it('works with mixins that have opted out of autobinding', function() {
|
||||
var mouseDidClick = jest.genMockFn();
|
||||
var mouseDidClick = jest.fn();
|
||||
|
||||
var TestMixin = {
|
||||
autobind: false,
|
||||
|
||||
@@ -43,7 +43,7 @@ describe('ReactClass-spec', function() {
|
||||
});
|
||||
|
||||
it('should copy prop types onto the Constructor', function() {
|
||||
var propValidator = jest.genMockFn();
|
||||
var propValidator = jest.fn();
|
||||
var TestComponent = React.createClass({
|
||||
propTypes: {
|
||||
value: propValidator,
|
||||
|
||||
@@ -25,8 +25,8 @@ describe('ReactClass-mixin', function() {
|
||||
beforeEach(function() {
|
||||
React = require('React');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
mixinPropValidator = jest.genMockFn();
|
||||
componentPropValidator = jest.genMockFn();
|
||||
mixinPropValidator = jest.fn();
|
||||
componentPropValidator = jest.fn();
|
||||
|
||||
var MixinA = {
|
||||
propTypes: {
|
||||
@@ -107,7 +107,7 @@ describe('ReactClass-mixin', function() {
|
||||
});
|
||||
|
||||
it('should support merging propTypes and statics', function() {
|
||||
var listener = jest.genMockFn();
|
||||
var listener = jest.fn();
|
||||
var instance = <TestComponent listener={listener} />;
|
||||
instance = ReactTestUtils.renderIntoDocument(instance);
|
||||
|
||||
@@ -122,7 +122,7 @@ describe('ReactClass-mixin', function() {
|
||||
});
|
||||
|
||||
it('should support chaining delegate functions', function() {
|
||||
var listener = jest.genMockFn();
|
||||
var listener = jest.fn();
|
||||
var instance = <TestComponent listener={listener} />;
|
||||
instance = ReactTestUtils.renderIntoDocument(instance);
|
||||
|
||||
@@ -135,7 +135,7 @@ describe('ReactClass-mixin', function() {
|
||||
});
|
||||
|
||||
it('should chain functions regardless of spec property order', function() {
|
||||
var listener = jest.genMockFn();
|
||||
var listener = jest.fn();
|
||||
var instance = <TestComponentWithReverseSpec listener={listener} />;
|
||||
instance = ReactTestUtils.renderIntoDocument(instance);
|
||||
|
||||
|
||||
@@ -113,6 +113,10 @@ var ReactElement = function(type, key, ref, self, source, owner, props) {
|
||||
return element;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create and return a new ReactElement of the given type.
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.createelement
|
||||
*/
|
||||
ReactElement.createElement = function(type, config, children) {
|
||||
var propName;
|
||||
|
||||
@@ -126,6 +130,13 @@ ReactElement.createElement = function(type, config, children) {
|
||||
|
||||
if (config != null) {
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
/* eslint-disable no-proto */
|
||||
config.__proto__ == null || config.__proto__ === Object.prototype,
|
||||
/* eslint-enable no-proto */
|
||||
'React.createElement(...): Expected props argument to be a plain object. ' +
|
||||
'Properties defined in its prototype chain will be ignored.'
|
||||
);
|
||||
ref = !config.hasOwnProperty('ref') ||
|
||||
Object.getOwnPropertyDescriptor(config, 'ref').get ? null : config.ref;
|
||||
key = !config.hasOwnProperty('key') ||
|
||||
@@ -223,6 +234,10 @@ ReactElement.createElement = function(type, config, children) {
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a function that produces ReactElements of a given type.
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.createfactory
|
||||
*/
|
||||
ReactElement.createFactory = function(type) {
|
||||
var factory = ReactElement.createElement.bind(null, type);
|
||||
// Expose the type on the factory and the prototype so that it can be
|
||||
@@ -248,6 +263,10 @@ ReactElement.cloneAndReplaceKey = function(oldElement, newKey) {
|
||||
return newElement;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clone and return a new ReactElement using element as the starting point.
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.cloneelement
|
||||
*/
|
||||
ReactElement.cloneElement = function(element, config, children) {
|
||||
var propName;
|
||||
|
||||
@@ -268,6 +287,15 @@ ReactElement.cloneElement = function(element, config, children) {
|
||||
var owner = element._owner;
|
||||
|
||||
if (config != null) {
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
/* eslint-disable no-proto */
|
||||
config.__proto__ == null || config.__proto__ === Object.prototype,
|
||||
/* eslint-enable no-proto */
|
||||
'React.cloneElement(...): Expected props argument to be a plain object. ' +
|
||||
'Properties defined in its prototype chain will be ignored.'
|
||||
);
|
||||
}
|
||||
if (config.ref !== undefined) {
|
||||
// Silently steal the ref from the parent.
|
||||
ref = config.ref;
|
||||
@@ -319,6 +347,8 @@ ReactElement.cloneElement = function(element, config, children) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Verifies the object is a ReactElement.
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#react.isvalidelement
|
||||
* @param {?object} object
|
||||
* @return {boolean} True if `object` is a valid component.
|
||||
* @final
|
||||
|
||||
@@ -138,6 +138,18 @@ describe('ReactElement', function() {
|
||||
expect(element.props.foo).toBe(1);
|
||||
});
|
||||
|
||||
it('warns if the config object inherits from any type other than Object', function() {
|
||||
spyOn(console, 'error');
|
||||
React.createElement('div', {foo: 1});
|
||||
expect(console.error).not.toHaveBeenCalled();
|
||||
React.createElement('div', Object.create({foo: 1}));
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'React.createElement(...): Expected props argument to be a plain object. ' +
|
||||
'Properties defined in its prototype chain will be ignored.'
|
||||
);
|
||||
});
|
||||
|
||||
it('extracts key and ref from the config', function() {
|
||||
var element = React.createFactory(ComponentClass)({
|
||||
key: '12',
|
||||
|
||||
@@ -66,6 +66,18 @@ describe('ReactElementClone', function() {
|
||||
expect(ReactDOM.findDOMNode(component).childNodes[0].className).toBe('xyz');
|
||||
});
|
||||
|
||||
it('should warn if the config object inherits from any type other than Object', function() {
|
||||
spyOn(console, 'error');
|
||||
React.cloneElement('div', {foo: 1});
|
||||
expect(console.error).not.toHaveBeenCalled();
|
||||
React.cloneElement('div', Object.create({foo: 1}));
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'React.cloneElement(...): Expected props argument to be a plain object. ' +
|
||||
'Properties defined in its prototype chain will be ignored.'
|
||||
);
|
||||
});
|
||||
|
||||
it('should keep the original ref if it is not overridden', function() {
|
||||
var Grandparent = React.createClass({
|
||||
render: function() {
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* 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 ReactComponentTreeDevtool
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
var tree = {};
|
||||
var rootIDs = [];
|
||||
|
||||
function updateTree(id, update) {
|
||||
if (!tree[id]) {
|
||||
tree[id] = {
|
||||
parentID: null,
|
||||
ownerID: null,
|
||||
text: null,
|
||||
childIDs: [],
|
||||
displayName: 'Unknown',
|
||||
isMounted: false,
|
||||
updateCount: 0,
|
||||
};
|
||||
}
|
||||
update(tree[id]);
|
||||
}
|
||||
|
||||
function purgeDeep(id) {
|
||||
var item = tree[id];
|
||||
if (item) {
|
||||
var {childIDs} = item;
|
||||
delete tree[id];
|
||||
childIDs.forEach(purgeDeep);
|
||||
}
|
||||
}
|
||||
|
||||
var ReactComponentTreeDevtool = {
|
||||
onSetDisplayName(id, displayName) {
|
||||
updateTree(id, item => item.displayName = displayName);
|
||||
},
|
||||
|
||||
onSetChildren(id, nextChildIDs) {
|
||||
updateTree(id, item => {
|
||||
var prevChildIDs = item.childIDs;
|
||||
item.childIDs = nextChildIDs;
|
||||
|
||||
nextChildIDs.forEach(nextChildID => {
|
||||
var nextChild = tree[nextChildID];
|
||||
invariant(
|
||||
nextChild,
|
||||
'Expected devtool events to fire for the child ' +
|
||||
'before its parent includes it in onSetChildren().'
|
||||
);
|
||||
invariant(
|
||||
nextChild.displayName != null,
|
||||
'Expected onSetDisplayName() to fire for the child ' +
|
||||
'before its parent includes it in onSetChildren().'
|
||||
);
|
||||
invariant(
|
||||
nextChild.childIDs != null || nextChild.text != null,
|
||||
'Expected onSetChildren() or onSetText() to fire for the child ' +
|
||||
'before its parent includes it in onSetChildren().'
|
||||
);
|
||||
invariant(
|
||||
nextChild.isMounted,
|
||||
'Expected onMountComponent() to fire for the child ' +
|
||||
'before its parent includes it in onSetChildren().'
|
||||
);
|
||||
|
||||
if (prevChildIDs.indexOf(nextChildID) === -1) {
|
||||
nextChild.parentID = id;
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
onSetOwner(id, ownerID) {
|
||||
updateTree(id, item => item.ownerID = ownerID);
|
||||
},
|
||||
|
||||
onSetText(id, text) {
|
||||
updateTree(id, item => item.text = text);
|
||||
},
|
||||
|
||||
onMountComponent(id) {
|
||||
updateTree(id, item => item.isMounted = true);
|
||||
},
|
||||
|
||||
onMountRootComponent(id) {
|
||||
rootIDs.push(id);
|
||||
},
|
||||
|
||||
onUpdateComponent(id) {
|
||||
updateTree(id, item => item.updateCount++);
|
||||
},
|
||||
|
||||
onUnmountComponent(id) {
|
||||
updateTree(id, item => item.isMounted = false);
|
||||
rootIDs = rootIDs.filter(rootID => rootID !== id);
|
||||
},
|
||||
|
||||
purgeUnmountedComponents() {
|
||||
if (ReactComponentTreeDevtool._preventPurging) {
|
||||
// Should only be used for testing.
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(tree)
|
||||
.filter(id => !tree[id].isMounted)
|
||||
.forEach(purgeDeep);
|
||||
},
|
||||
|
||||
isMounted(id) {
|
||||
var item = tree[id];
|
||||
return item ? item.isMounted : false;
|
||||
},
|
||||
|
||||
getChildIDs(id) {
|
||||
var item = tree[id];
|
||||
return item ? item.childIDs : [];
|
||||
},
|
||||
|
||||
getDisplayName(id) {
|
||||
var item = tree[id];
|
||||
return item ? item.displayName : 'Unknown';
|
||||
},
|
||||
|
||||
getOwnerID(id) {
|
||||
var item = tree[id];
|
||||
return item ? item.ownerID : null;
|
||||
},
|
||||
|
||||
getParentID(id) {
|
||||
var item = tree[id];
|
||||
return item ? item.parentID : null;
|
||||
},
|
||||
|
||||
getText(id) {
|
||||
var item = tree[id];
|
||||
return item ? item.text : null;
|
||||
},
|
||||
|
||||
getUpdateCount(id) {
|
||||
var item = tree[id];
|
||||
return item ? item.updateCount : 0;
|
||||
},
|
||||
|
||||
getRootIDs() {
|
||||
return rootIDs;
|
||||
},
|
||||
|
||||
getRegisteredIDs() {
|
||||
return Object.keys(tree);
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = ReactComponentTreeDevtool;
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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 ReactNativeOperationHistoryDevtool
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var history = [];
|
||||
|
||||
var ReactNativeOperationHistoryDevtool = {
|
||||
onNativeOperation(debugID, type, payload) {
|
||||
history.push({
|
||||
instanceID: debugID,
|
||||
type,
|
||||
payload,
|
||||
});
|
||||
},
|
||||
|
||||
clearHistory() {
|
||||
if (ReactNativeOperationHistoryDevtool._preventClearing) {
|
||||
// Should only be used for tests.
|
||||
return;
|
||||
}
|
||||
|
||||
history = [];
|
||||
},
|
||||
|
||||
getHistory() {
|
||||
return history;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = ReactNativeOperationHistoryDevtool;
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,724 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
var React;
|
||||
var ReactDOM;
|
||||
var ReactDOMComponentTree;
|
||||
var ReactDOMFeatureFlags;
|
||||
var ReactNativeOperationHistoryDevtool;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModuleRegistry();
|
||||
|
||||
React = require('React');
|
||||
ReactDOM = require('ReactDOM');
|
||||
ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
|
||||
ReactNativeOperationHistoryDevtool = require('ReactNativeOperationHistoryDevtool');
|
||||
});
|
||||
|
||||
function assertHistoryMatches(expectedHistory) {
|
||||
var actualHistory = ReactNativeOperationHistoryDevtool.getHistory();
|
||||
expect(actualHistory).toEqual(expectedHistory);
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
it('gets recorded for native roots', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div><p>Hi.</p></div>, node);
|
||||
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: ReactDOMFeatureFlags.useCreateElement ?
|
||||
'DIV' :
|
||||
'<div data-reactroot="" data-reactid="1"><p data-reactid="2">Hi.</p></div>',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets recorded for composite roots', () => {
|
||||
function Foo() {
|
||||
return <div><p>Hi.</p></div>;
|
||||
}
|
||||
var node = document.createElement('div');
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: ReactDOMFeatureFlags.useCreateElement ?
|
||||
'DIV' :
|
||||
'<div data-reactroot="" data-reactid="1">' +
|
||||
'<p data-reactid="2">Hi.</p></div>',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets ignored for composite roots that return null', () => {
|
||||
function Foo() {
|
||||
return null;
|
||||
}
|
||||
var node = document.createElement('div');
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
// Empty DOM components should be invisible to devtools.
|
||||
assertHistoryMatches([]);
|
||||
});
|
||||
|
||||
it('gets recorded when a native is mounted deeply instead of null', () => {
|
||||
var element;
|
||||
function Foo() {
|
||||
return element;
|
||||
}
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
|
||||
var node = document.createElement('div');
|
||||
element = null;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
element = <span />;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
// Since empty components should be invisible to devtools,
|
||||
// we record a "mount" event rather than a "replace with".
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: 'SPAN',
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('update styles', () => {
|
||||
it('gets recorded during mount', () => {
|
||||
var node = document.createElement('div');
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div style={{
|
||||
color: 'red',
|
||||
backgroundColor: 'yellow',
|
||||
}} />, node);
|
||||
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
if (ReactDOMFeatureFlags.useCreateElement) {
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update styles',
|
||||
payload: {
|
||||
color: 'red',
|
||||
backgroundColor: 'yellow',
|
||||
},
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: 'DIV',
|
||||
}]);
|
||||
} else {
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: '<div style="color:red;background-color:yellow;" ' +
|
||||
'data-reactroot="" data-reactid="1"></div>',
|
||||
}]);
|
||||
}
|
||||
});
|
||||
|
||||
it('gets recorded during an update', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div style={{ color: 'red' }} />, node);
|
||||
ReactDOM.render(<div style={{
|
||||
color: 'blue',
|
||||
backgroundColor: 'yellow',
|
||||
}} />, node);
|
||||
ReactDOM.render(<div style={{ backgroundColor: 'green' }} />, node);
|
||||
ReactDOM.render(<div />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update styles',
|
||||
payload: { color: 'red' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update styles',
|
||||
payload: { color: 'blue', backgroundColor: 'yellow' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update styles',
|
||||
payload: { color: '', backgroundColor: 'green' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update styles',
|
||||
payload: { backgroundColor: '' },
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets ignored if the styles are shallowly equal', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div style={{
|
||||
color: 'red',
|
||||
backgroundColor: 'yellow',
|
||||
}} />, node);
|
||||
ReactDOM.render(<div style={{
|
||||
color: 'red',
|
||||
backgroundColor: 'yellow',
|
||||
}} />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update styles',
|
||||
payload: {
|
||||
color: 'red',
|
||||
backgroundColor: 'yellow',
|
||||
},
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('update attribute', () => {
|
||||
describe('simple attribute', () => {
|
||||
it('gets recorded during mount', () => {
|
||||
var node = document.createElement('div');
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div className="rad" tabIndex={42} />, node);
|
||||
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
if (ReactDOMFeatureFlags.useCreateElement) {
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { className: 'rad' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { tabIndex: 42 },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: 'DIV',
|
||||
}]);
|
||||
} else {
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: '<div class="rad" tabindex="42" data-reactroot="" ' +
|
||||
'data-reactid="1"></div>',
|
||||
}]);
|
||||
}
|
||||
});
|
||||
|
||||
it('gets recorded during an update', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div className="rad" />, node);
|
||||
ReactDOM.render(<div className="mad" tabIndex={42} />, node);
|
||||
ReactDOM.render(<div tabIndex={43} />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { className: 'rad' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { className: 'mad' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { tabIndex: 42 },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove attribute',
|
||||
payload: 'className',
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { tabIndex: 43 },
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('attribute that gets removed with certain values', () => {
|
||||
it('gets recorded as a removal during an update', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div disabled={true} />, node);
|
||||
ReactDOM.render(<div disabled={false} />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { disabled: true },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove attribute',
|
||||
payload: 'disabled',
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('custom attribute', () => {
|
||||
it('gets recorded during mount', () => {
|
||||
var node = document.createElement('div');
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div data-x="rad" data-y={42} />, node);
|
||||
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
if (ReactDOMFeatureFlags.useCreateElement) {
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { 'data-x': 'rad' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { 'data-y': 42 },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: 'DIV',
|
||||
}]);
|
||||
} else {
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: '<div data-x="rad" data-y="42" data-reactroot="" ' +
|
||||
'data-reactid="1"></div>',
|
||||
}]);
|
||||
}
|
||||
});
|
||||
|
||||
it('gets recorded during an update', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div data-x="rad" />, node);
|
||||
ReactDOM.render(<div data-x="mad" data-y={42} />, node);
|
||||
ReactDOM.render(<div data-y={43} />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { 'data-x': 'rad' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { 'data-x': 'mad' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { 'data-y': 42 },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove attribute',
|
||||
payload: 'data-x',
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { 'data-y': 43 },
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('attribute on a web component', () => {
|
||||
it('gets recorded during mount', () => {
|
||||
var node = document.createElement('div');
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<my-component className="rad" tabIndex={42} />, node);
|
||||
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
if (ReactDOMFeatureFlags.useCreateElement) {
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { className: 'rad' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { tabIndex: 42 },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: 'MY-COMPONENT',
|
||||
}]);
|
||||
} else {
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
payload: '<my-component className="rad" tabIndex="42" ' +
|
||||
'data-reactroot="" data-reactid="1"></my-component>',
|
||||
}]);
|
||||
}
|
||||
});
|
||||
|
||||
it('gets recorded during an update', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<my-component />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<my-component className="rad" />, node);
|
||||
ReactDOM.render(<my-component className="mad" tabIndex={42} />, node);
|
||||
ReactDOM.render(<my-component tabIndex={43} />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { className: 'rad' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { className: 'mad' },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { tabIndex: 42 },
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove attribute',
|
||||
payload: 'className',
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
payload: { tabIndex: 43 },
|
||||
}]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('replace text', () => {
|
||||
describe('text content', () => {
|
||||
it('gets recorded during an update from text content', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div>Hi.</div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div>Bye.</div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace text',
|
||||
payload: 'Bye.',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets recorded during an update from html', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div dangerouslySetInnerHTML={{__html: 'Hi.'}} />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div>Bye.</div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace text',
|
||||
payload: 'Bye.',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets recorded during an update from children', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div><span /><p /></div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div>Bye.</div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove child',
|
||||
payload: {fromIndex: 0},
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove child',
|
||||
payload: {fromIndex: 1},
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace text',
|
||||
payload: 'Bye.',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets ignored if new text is equal', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div>Hi.</div>, node);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div>Hi.</div>, node);
|
||||
|
||||
assertHistoryMatches([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('text node', () => {
|
||||
it('gets recorded during an update', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div>{'Hi.'}{42}</div>, node);
|
||||
var inst1 = ReactDOMComponentTree.getInstanceFromNode(node.firstChild.childNodes[0]);
|
||||
var inst2 = ReactDOMComponentTree.getInstanceFromNode(node.firstChild.childNodes[3]);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div>{'Bye.'}{43}</div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst1._debugID,
|
||||
type: 'replace text',
|
||||
payload: 'Bye.',
|
||||
}, {
|
||||
instanceID: inst2._debugID,
|
||||
type: 'replace text',
|
||||
payload: '43',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets ignored if new text is equal', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div>{'Hi.'}{42}</div>, node);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div>{'Hi.'}{42}</div>, node);
|
||||
|
||||
assertHistoryMatches([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('replace with', () => {
|
||||
it('gets recorded when composite renders to a different type', () => {
|
||||
var element;
|
||||
function Foo() {
|
||||
return element;
|
||||
}
|
||||
|
||||
var node = document.createElement('div');
|
||||
element = <div />;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
element = <span />;
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace with',
|
||||
payload: 'SPAN',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets recorded when composite renders to null after a native', () => {
|
||||
var element;
|
||||
function Foo() {
|
||||
return element;
|
||||
}
|
||||
|
||||
var node = document.createElement('div');
|
||||
element = <span />;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
element = null;
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace with',
|
||||
payload: '#comment',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets ignored if the type has not changed', () => {
|
||||
var element;
|
||||
function Foo() {
|
||||
return element;
|
||||
}
|
||||
|
||||
var node = document.createElement('div');
|
||||
element = <div />;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
element = <div />;
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
assertHistoryMatches([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('replace children', () => {
|
||||
it('gets recorded during an update from text content', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div>Hi.</div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(
|
||||
<div dangerouslySetInnerHTML={{__html: 'Bye.'}} />,
|
||||
node
|
||||
);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace children',
|
||||
payload: 'Bye.',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets recorded during an update from html', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(
|
||||
<div dangerouslySetInnerHTML={{__html: 'Hi.'}} />,
|
||||
node
|
||||
);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(
|
||||
<div dangerouslySetInnerHTML={{__html: 'Bye.'}} />,
|
||||
node
|
||||
);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace children',
|
||||
payload: 'Bye.',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets recorded during an update from children', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div><span /><p /></div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(
|
||||
<div dangerouslySetInnerHTML={{__html: 'Hi.'}} />,
|
||||
node
|
||||
);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove child',
|
||||
payload: {fromIndex: 0},
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove child',
|
||||
payload: {fromIndex: 1},
|
||||
}, {
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace children',
|
||||
payload: 'Hi.',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('gets ignored if new html is equal', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(
|
||||
<div dangerouslySetInnerHTML={{__html: 'Hi.'}} />,
|
||||
node
|
||||
);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(
|
||||
<div dangerouslySetInnerHTML={{__html: 'Hi.'}} />,
|
||||
node
|
||||
);
|
||||
|
||||
assertHistoryMatches([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('insert child', () => {
|
||||
it('gets reported when a child is inserted', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div><span /></div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div><span /><p /></div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'insert child',
|
||||
payload: {toIndex: 1, content: 'P'},
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('move child', () => {
|
||||
it('gets reported when a child is inserted', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div><span key="a" /><p key="b" /></div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div><p key="b" /><span key="a" /></div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'move child',
|
||||
payload: {fromIndex: 0, toIndex: 1},
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove child', () => {
|
||||
it('gets reported when a child is removed', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div><span key="a" /><p key="b" /></div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div><span key="a" /></div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove child',
|
||||
payload: {fromIndex: 1},
|
||||
}]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -16,7 +16,6 @@
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
var ReactDefaultInjection = require('ReactDefaultInjection');
|
||||
var ReactMount = require('ReactMount');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var ReactReconciler = require('ReactReconciler');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
var ReactVersion = require('ReactVersion');
|
||||
@@ -28,11 +27,9 @@ var warning = require('warning');
|
||||
|
||||
ReactDefaultInjection.inject();
|
||||
|
||||
var render = ReactPerf.measure('React', 'render', ReactMount.render);
|
||||
|
||||
var React = {
|
||||
findDOMNode: findDOMNode,
|
||||
render: render,
|
||||
render: ReactMount.render,
|
||||
unmountComponentAtNode: ReactMount.unmountComponentAtNode,
|
||||
version: ReactVersion,
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
var DOMChildrenOperations = require('DOMChildrenOperations');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
|
||||
/**
|
||||
* Operations used to process updates to DOM nodes.
|
||||
@@ -32,8 +31,4 @@ var ReactDOMIDOperations = {
|
||||
},
|
||||
};
|
||||
|
||||
ReactPerf.measureMethods(ReactDOMIDOperations, 'ReactDOMIDOperations', {
|
||||
dangerouslyProcessChildrenUpdates: 'dangerouslyProcessChildrenUpdates',
|
||||
});
|
||||
|
||||
module.exports = ReactDOMIDOperations;
|
||||
|
||||
@@ -22,7 +22,6 @@ var ReactElement = require('ReactElement');
|
||||
var ReactFeatureFlags = require('ReactFeatureFlags');
|
||||
var ReactInstrumentation = require('ReactInstrumentation');
|
||||
var ReactMarkupChecksum = require('ReactMarkupChecksum');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var ReactReconciler = require('ReactReconciler');
|
||||
var ReactUpdateQueue = require('ReactUpdateQueue');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
@@ -308,6 +307,10 @@ var ReactMount = {
|
||||
shouldReuseMarkup,
|
||||
context
|
||||
) {
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onBeginFlush();
|
||||
}
|
||||
|
||||
// Various parts of our code (such as ReactCompositeComponent's
|
||||
// _renderValidatedComponent) assume that calls to render aren't nested;
|
||||
// verify that that's the case.
|
||||
@@ -333,6 +336,12 @@ var ReactMount = {
|
||||
ReactBrowserEventEmitter.ensureScrollValueMonitoring();
|
||||
var componentInstance = instantiateReactComponent(nextElement);
|
||||
|
||||
if (__DEV__) {
|
||||
// Mute future events from the top level wrapper.
|
||||
// It is an implementation detail that devtools should not know about.
|
||||
componentInstance._debugID = 0;
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -349,7 +358,11 @@ var ReactMount = {
|
||||
instancesByReactRootID[wrapperID] = componentInstance;
|
||||
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onMountRootComponent(componentInstance);
|
||||
// The instance here is TopLevelWrapper so we report mount for its child.
|
||||
ReactInstrumentation.debugTool.onMountRootComponent(
|
||||
componentInstance._renderedComponent._debugID
|
||||
);
|
||||
ReactInstrumentation.debugTool.onEndFlush();
|
||||
}
|
||||
|
||||
return componentInstance;
|
||||
@@ -497,6 +510,7 @@ var ReactMount = {
|
||||
|
||||
/**
|
||||
* Renders a React component into the DOM in the supplied `container`.
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#reactdom.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
|
||||
@@ -513,6 +527,7 @@ var ReactMount = {
|
||||
|
||||
/**
|
||||
* Unmounts and destroys the React component rendered in the `container`.
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#reactdom.unmountcomponentatnode
|
||||
*
|
||||
* @param {DOMElement} container DOM element containing a React component.
|
||||
* @return {boolean} True if a component was found in and unmounted from
|
||||
@@ -684,12 +699,18 @@ var ReactMount = {
|
||||
setInnerHTML(container, markup);
|
||||
ReactDOMComponentTree.precacheNode(instance, container.firstChild);
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
var nativeNode = ReactDOMComponentTree.getInstanceFromNode(container.firstChild);
|
||||
if (nativeNode._debugID !== 0) {
|
||||
ReactInstrumentation.debugTool.onNativeOperation(
|
||||
nativeNode._debugID,
|
||||
'mount',
|
||||
markup.toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
ReactPerf.measureMethods(ReactMount, 'ReactMount', {
|
||||
_renderNewRootComponent: '_renderNewRootComponent',
|
||||
_mountImageIntoNode: '_mountImageIntoNode',
|
||||
});
|
||||
|
||||
module.exports = ReactMount;
|
||||
|
||||
@@ -35,7 +35,7 @@ var recordIDAndReturnFalse = function(id, event) {
|
||||
recordID(id);
|
||||
return false;
|
||||
};
|
||||
var LISTENER = jest.genMockFn();
|
||||
var LISTENER = jest.fn();
|
||||
var ON_CLICK_KEY = keyOf({onClick: null});
|
||||
var ON_TOUCH_TAP_KEY = keyOf({onTouchTap: null});
|
||||
var ON_CHANGE_KEY = keyOf({onChange: null});
|
||||
@@ -284,7 +284,7 @@ describe('ReactBrowserEventEmitter', function() {
|
||||
*/
|
||||
|
||||
it('should invoke handlers that were removed while bubbling', function() {
|
||||
var handleParentClick = jest.genMockFn();
|
||||
var handleParentClick = jest.fn();
|
||||
var handleChildClick = function(event) {
|
||||
EventPluginHub.deleteAllListeners(getInternal(PARENT));
|
||||
};
|
||||
@@ -303,7 +303,7 @@ describe('ReactBrowserEventEmitter', function() {
|
||||
});
|
||||
|
||||
it('should not invoke newly inserted handlers while bubbling', function() {
|
||||
var handleParentClick = jest.genMockFn();
|
||||
var handleParentClick = jest.fn();
|
||||
var handleChildClick = function(event) {
|
||||
EventPluginHub.putListener(
|
||||
getInternal(PARENT),
|
||||
|
||||
@@ -12,15 +12,18 @@
|
||||
'use strict';
|
||||
|
||||
describe('ReactDOMIDOperations', function() {
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
var ReactDOMIDOperations = require('ReactDOMIDOperations');
|
||||
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
|
||||
|
||||
it('should update innerHTML and preserve whitespace', function() {
|
||||
var stubNode = document.createElement('div');
|
||||
var html = '\n \t <span> \n testContent \t </span> \n \t';
|
||||
var stubInstance = {_debugID: 1};
|
||||
ReactDOMComponentTree.precacheNode(stubInstance, stubNode);
|
||||
|
||||
var html = '\n \t <span> \n testContent \t </span> \n \t';
|
||||
ReactDOMIDOperations.dangerouslyProcessChildrenUpdates(
|
||||
{_nativeNode: stubNode},
|
||||
stubInstance,
|
||||
[{
|
||||
type: ReactMultiChildUpdateTypes.SET_MARKUP,
|
||||
content: html,
|
||||
|
||||
@@ -30,7 +30,7 @@ describe('ReactEventListener', function() {
|
||||
ReactEventListener = require('ReactEventListener');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
|
||||
handleTopLevel = jest.genMockFn();
|
||||
handleTopLevel = jest.fn();
|
||||
ReactEventListener._handleTopLevel = handleTopLevel;
|
||||
});
|
||||
|
||||
|
||||
@@ -87,8 +87,8 @@ describe('ReactMount', function() {
|
||||
it('should unmount and remount if the key changes', function() {
|
||||
var container = document.createElement('container');
|
||||
|
||||
var mockMount = jest.genMockFn();
|
||||
var mockUnmount = jest.genMockFn();
|
||||
var mockMount = jest.fn();
|
||||
var mockUnmount = jest.fn();
|
||||
|
||||
var Component = React.createClass({
|
||||
componentDidMount: mockMount,
|
||||
|
||||
@@ -66,7 +66,7 @@ describe('SelectEventPlugin', function() {
|
||||
},
|
||||
});
|
||||
|
||||
var cb = jest.genMockFn();
|
||||
var cb = jest.fn();
|
||||
|
||||
var rendered = ReactTestUtils.renderIntoDocument(
|
||||
<WithSelect onSelect={cb} />
|
||||
|
||||
@@ -22,6 +22,8 @@ var warning = require('warning');
|
||||
/**
|
||||
* Returns the DOM node rendered by this element.
|
||||
*
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#reactdom.finddomnode
|
||||
*
|
||||
* @param {ReactComponent|DOMElement} componentOrElement
|
||||
* @return {?DOMElement} The root node of this element.
|
||||
*/
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
var DOMLazyTree = require('DOMLazyTree');
|
||||
var Danger = require('Danger');
|
||||
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
var ReactInstrumentation = require('ReactInstrumentation');
|
||||
|
||||
var createMicrosoftUnsafeLocalFunction = require('createMicrosoftUnsafeLocalFunction');
|
||||
var setInnerHTML = require('setInnerHTML');
|
||||
@@ -120,6 +121,37 @@ function replaceDelimitedText(openingComment, closingComment, stringText) {
|
||||
removeDelimitedText(parentNode, openingComment, closingComment);
|
||||
}
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onNativeOperation(
|
||||
ReactDOMComponentTree.getInstanceFromNode(openingComment)._debugID,
|
||||
'replace text',
|
||||
stringText
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var dangerouslyReplaceNodeWithMarkup = Danger.dangerouslyReplaceNodeWithMarkup;
|
||||
if (__DEV__) {
|
||||
dangerouslyReplaceNodeWithMarkup = function(oldChild, markup, prevInstance) {
|
||||
Danger.dangerouslyReplaceNodeWithMarkup(oldChild, markup);
|
||||
if (prevInstance._debugID !== 0) {
|
||||
ReactInstrumentation.debugTool.onNativeOperation(
|
||||
prevInstance._debugID,
|
||||
'replace with',
|
||||
markup.toString()
|
||||
);
|
||||
} else {
|
||||
var nextInstance = ReactDOMComponentTree.getInstanceFromNode(markup.node);
|
||||
if (nextInstance._debugID !== 0) {
|
||||
ReactInstrumentation.debugTool.onNativeOperation(
|
||||
nextInstance._debugID,
|
||||
'mount',
|
||||
markup.toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,7 +159,7 @@ function replaceDelimitedText(openingComment, closingComment, stringText) {
|
||||
*/
|
||||
var DOMChildrenOperations = {
|
||||
|
||||
dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup,
|
||||
dangerouslyReplaceNodeWithMarkup: dangerouslyReplaceNodeWithMarkup,
|
||||
|
||||
replaceDelimitedText: replaceDelimitedText,
|
||||
|
||||
@@ -139,6 +171,11 @@ var DOMChildrenOperations = {
|
||||
* @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) {
|
||||
@@ -148,6 +185,13 @@ var DOMChildrenOperations = {
|
||||
update.content,
|
||||
getNodeAfter(parentNode, update.afterNode)
|
||||
);
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onNativeOperation(
|
||||
parentNodeDebugID,
|
||||
'insert child',
|
||||
{toIndex: update.toIndex, content: update.content.toString()}
|
||||
);
|
||||
}
|
||||
break;
|
||||
case ReactMultiChildUpdateTypes.MOVE_EXISTING:
|
||||
moveChild(
|
||||
@@ -155,21 +199,49 @@ var DOMChildrenOperations = {
|
||||
update.fromNode,
|
||||
getNodeAfter(parentNode, update.afterNode)
|
||||
);
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onNativeOperation(
|
||||
parentNodeDebugID,
|
||||
'move child',
|
||||
{fromIndex: update.fromIndex, toIndex: update.toIndex}
|
||||
);
|
||||
}
|
||||
break;
|
||||
case ReactMultiChildUpdateTypes.SET_MARKUP:
|
||||
setInnerHTML(
|
||||
parentNode,
|
||||
update.content
|
||||
);
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onNativeOperation(
|
||||
parentNodeDebugID,
|
||||
'replace children',
|
||||
update.content.toString()
|
||||
);
|
||||
}
|
||||
break;
|
||||
case ReactMultiChildUpdateTypes.TEXT_CONTENT:
|
||||
setTextContent(
|
||||
parentNode,
|
||||
update.content
|
||||
);
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onNativeOperation(
|
||||
parentNodeDebugID,
|
||||
'replace text',
|
||||
update.content.toString()
|
||||
);
|
||||
}
|
||||
break;
|
||||
case ReactMultiChildUpdateTypes.REMOVE_NODE:
|
||||
removeChild(parentNode, update.fromNode);
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onNativeOperation(
|
||||
parentNodeDebugID,
|
||||
'remove child',
|
||||
{fromIndex: update.fromIndex}
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -177,8 +249,4 @@ var DOMChildrenOperations = {
|
||||
|
||||
};
|
||||
|
||||
ReactPerf.measureMethods(DOMChildrenOperations, 'DOMChildrenOperations', {
|
||||
replaceDelimitedText: 'replaceDelimitedText',
|
||||
});
|
||||
|
||||
module.exports = DOMChildrenOperations;
|
||||
|
||||
@@ -11,9 +11,14 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var DOMNamespaces = require('DOMNamespaces');
|
||||
|
||||
var createMicrosoftUnsafeLocalFunction = require('createMicrosoftUnsafeLocalFunction');
|
||||
var setTextContent = require('setTextContent');
|
||||
|
||||
var ELEMENT_NODE_TYPE = 1;
|
||||
var DOCUMENT_FRAGMENT_NODE_TYPE = 11;
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -56,8 +61,15 @@ var insertTreeBefore = createMicrosoftUnsafeLocalFunction(
|
||||
// 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.
|
||||
if (tree.node.nodeType === 11) {
|
||||
// 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_TYPE
|
||||
||
|
||||
tree.node.nodeType === ELEMENT_NODE_TYPE &&
|
||||
tree.node.nodeName.toLowerCase() === 'object' &&
|
||||
(tree.node.namespaceURI == null ||
|
||||
tree.node.namespaceURI === DOMNamespaces.html)) {
|
||||
insertTreeChildren(tree);
|
||||
parentNode.insertBefore(tree.node, referenceNode);
|
||||
} else {
|
||||
@@ -96,12 +108,17 @@ function queueText(tree, text) {
|
||||
}
|
||||
}
|
||||
|
||||
function toString() {
|
||||
return this.node.nodeName;
|
||||
}
|
||||
|
||||
function DOMLazyTree(node) {
|
||||
return {
|
||||
node: node,
|
||||
children: [],
|
||||
html: null,
|
||||
text: null,
|
||||
toString,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -92,6 +92,8 @@ var ReactDOMInput = {
|
||||
inst._currentElement._owner
|
||||
);
|
||||
|
||||
var owner = inst._currentElement._owner;
|
||||
|
||||
if (props.valueLink !== undefined && !didWarnValueLink) {
|
||||
warning(
|
||||
false,
|
||||
@@ -113,11 +115,14 @@ var ReactDOMInput = {
|
||||
) {
|
||||
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'
|
||||
'https://fb.me/react-controlled-components',
|
||||
owner && owner.getName() || 'A component',
|
||||
props.type
|
||||
);
|
||||
didWarnCheckedDefaultChecked = true;
|
||||
}
|
||||
@@ -128,11 +133,14 @@ var ReactDOMInput = {
|
||||
) {
|
||||
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'
|
||||
'https://fb.me/react-controlled-components',
|
||||
owner && owner.getName() || 'A component',
|
||||
props.type
|
||||
);
|
||||
didWarnValueDefaultValue = true;
|
||||
}
|
||||
@@ -169,7 +177,7 @@ var ReactDOMInput = {
|
||||
) {
|
||||
warning(
|
||||
false,
|
||||
'%s is changing a uncontrolled input of type %s to be controlled. ' +
|
||||
'%s 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',
|
||||
|
||||
@@ -36,7 +36,7 @@ describe('DisabledInputUtils', function() {
|
||||
return element;
|
||||
}
|
||||
|
||||
var onClick = jest.genMockFn();
|
||||
var onClick = jest.fn();
|
||||
|
||||
elements.forEach(function(tagName) {
|
||||
|
||||
|
||||
@@ -236,7 +236,7 @@ describe('ReactDOMInput', function() {
|
||||
});
|
||||
|
||||
it('should support ReactLink', function() {
|
||||
var link = new ReactLink('yolo', jest.genMockFn());
|
||||
var link = new ReactLink('yolo', jest.fn());
|
||||
var instance = <input type="text" valueLink={link} />;
|
||||
|
||||
instance = ReactTestUtils.renderIntoDocument(instance);
|
||||
@@ -253,7 +253,7 @@ describe('ReactDOMInput', function() {
|
||||
});
|
||||
|
||||
it('should warn with value and no onChange handler', function() {
|
||||
var link = new ReactLink('yolo', jest.genMockFn());
|
||||
var link = new ReactLink('yolo', jest.fn());
|
||||
ReactTestUtils.renderIntoDocument(<input type="text" valueLink={link} />);
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
@@ -261,7 +261,7 @@ describe('ReactDOMInput', function() {
|
||||
);
|
||||
|
||||
ReactTestUtils.renderIntoDocument(
|
||||
<input type="text" value="zoink" onChange={jest.genMockFn()} />
|
||||
<input type="text" value="zoink" onChange={jest.fn()} />
|
||||
);
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
ReactTestUtils.renderIntoDocument(<input type="text" value="zoink" />);
|
||||
@@ -293,7 +293,7 @@ describe('ReactDOMInput', function() {
|
||||
|
||||
it('should throw if both value and valueLink are provided', function() {
|
||||
var node = document.createElement('div');
|
||||
var link = new ReactLink('yolo', jest.genMockFn());
|
||||
var link = new ReactLink('yolo', jest.fn());
|
||||
var instance = <input type="text" valueLink={link} />;
|
||||
|
||||
expect(() => ReactDOM.render(instance, node)).not.toThrow();
|
||||
@@ -313,7 +313,7 @@ describe('ReactDOMInput', function() {
|
||||
});
|
||||
|
||||
it('should support checkedLink', function() {
|
||||
var link = new ReactLink(true, jest.genMockFn());
|
||||
var link = new ReactLink(true, jest.fn());
|
||||
var instance = <input type="checkbox" checkedLink={link} />;
|
||||
|
||||
instance = ReactTestUtils.renderIntoDocument(instance);
|
||||
@@ -331,7 +331,7 @@ describe('ReactDOMInput', function() {
|
||||
|
||||
it('should warn with checked and no onChange handler', function() {
|
||||
var node = document.createElement('div');
|
||||
var link = new ReactLink(true, jest.genMockFn());
|
||||
var link = new ReactLink(true, jest.fn());
|
||||
ReactDOM.render(<input type="checkbox" checkedLink={link} />, node);
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
@@ -342,7 +342,7 @@ describe('ReactDOMInput', function() {
|
||||
<input
|
||||
type="checkbox"
|
||||
checked="false"
|
||||
onChange={jest.genMockFn()}
|
||||
onChange={jest.fn()}
|
||||
/>
|
||||
);
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
@@ -370,7 +370,7 @@ describe('ReactDOMInput', function() {
|
||||
|
||||
it('should throw if both checked and checkedLink are provided', function() {
|
||||
var node = document.createElement('div');
|
||||
var link = new ReactLink(true, jest.genMockFn());
|
||||
var link = new ReactLink(true, jest.fn());
|
||||
var instance = <input type="checkbox" checkedLink={link} />;
|
||||
|
||||
expect(() => ReactDOM.render(instance, node)).not.toThrow();
|
||||
@@ -392,7 +392,7 @@ describe('ReactDOMInput', function() {
|
||||
|
||||
it('should throw if both checkedLink and valueLink are provided', function() {
|
||||
var node = document.createElement('div');
|
||||
var link = new ReactLink(true, jest.genMockFn());
|
||||
var link = new ReactLink(true, jest.fn());
|
||||
var instance = <input type="checkbox" checkedLink={link} />;
|
||||
|
||||
expect(() => ReactDOM.render(instance, node)).not.toThrow();
|
||||
@@ -422,6 +422,7 @@ describe('ReactDOMInput', function() {
|
||||
<input type="radio" checked={true} defaultChecked={true} readOnly={true} />
|
||||
);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'A component contains an input of type radio 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 ' +
|
||||
@@ -440,6 +441,7 @@ describe('ReactDOMInput', function() {
|
||||
<input type="text" value="foo" defaultValue="bar" readOnly={true} />
|
||||
);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'A component contains an input of type text 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 ' +
|
||||
@@ -488,7 +490,7 @@ describe('ReactDOMInput', function() {
|
||||
ReactDOM.render(<input type="text" value="controlled" />, container);
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'A component is changing a uncontrolled input of type text to be controlled. ' +
|
||||
'A component is changing an uncontrolled input of type text 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'
|
||||
@@ -530,7 +532,7 @@ describe('ReactDOMInput', function() {
|
||||
ReactDOM.render(<input type="checkbox" checked={true} />, container);
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'A component is changing a uncontrolled input of type checkbox to be controlled. ' +
|
||||
'A component is changing an uncontrolled input of type checkbox 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'
|
||||
@@ -572,7 +574,7 @@ describe('ReactDOMInput', function() {
|
||||
ReactDOM.render(<input type="radio" checked={true} />, container);
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'A component is changing a uncontrolled input of type radio to be controlled. ' +
|
||||
'A component is changing an uncontrolled input of type radio 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'
|
||||
|
||||
@@ -348,7 +348,7 @@ describe('ReactDOMSelect', function() {
|
||||
});
|
||||
|
||||
it('should support ReactLink', function() {
|
||||
var link = new ReactLink('giraffe', jest.genMockFn());
|
||||
var link = new ReactLink('giraffe', jest.fn());
|
||||
var stub =
|
||||
<select valueLink={link}>
|
||||
<option value="monkey">A monkey!</option>
|
||||
|
||||
@@ -237,7 +237,7 @@ describe('ReactDOMTextarea', function() {
|
||||
});
|
||||
|
||||
it('should support ReactLink', function() {
|
||||
var link = new ReactLink('yolo', jest.genMockFn());
|
||||
var link = new ReactLink('yolo', jest.fn());
|
||||
var instance = <textarea valueLink={link} />;
|
||||
|
||||
spyOn(console, 'error');
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
var ReactDOMContainerInfo = require('ReactDOMContainerInfo');
|
||||
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactInstrumentation = require('ReactInstrumentation');
|
||||
var ReactMarkupChecksum = require('ReactMarkupChecksum');
|
||||
var ReactReconciler = require('ReactReconciler');
|
||||
var ReactServerBatchingStrategy = require('ReactServerBatchingStrategy');
|
||||
var ReactServerRenderingTransaction =
|
||||
require('ReactServerRenderingTransaction');
|
||||
@@ -35,13 +37,23 @@ function renderToStringImpl(element, makeStaticMarkup) {
|
||||
transaction = ReactServerRenderingTransaction.getPooled(makeStaticMarkup);
|
||||
|
||||
return transaction.perform(function() {
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onBeginFlush();
|
||||
}
|
||||
var componentInstance = instantiateReactComponent(element);
|
||||
var markup = componentInstance.mountComponent(
|
||||
var markup = ReactReconciler.mountComponent(
|
||||
componentInstance,
|
||||
transaction,
|
||||
null,
|
||||
ReactDOMContainerInfo(),
|
||||
emptyObject
|
||||
);
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onUnmountComponent(
|
||||
componentInstance._debugID
|
||||
);
|
||||
ReactInstrumentation.debugTool.onEndFlush();
|
||||
}
|
||||
if (!makeStaticMarkup) {
|
||||
markup = ReactMarkupChecksum.addChecksumToMarkup(markup);
|
||||
}
|
||||
@@ -55,6 +67,11 @@ function renderToStringImpl(element, makeStaticMarkup) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a ReactElement to its initial HTML. This should only be used on the
|
||||
* server.
|
||||
* See https://facebook.github.io/react/docs/top-level-api.html#reactdomserver.rendertostring
|
||||
*/
|
||||
function renderToString(element) {
|
||||
invariant(
|
||||
ReactElement.isValidElement(element),
|
||||
@@ -63,6 +80,11 @@ function renderToString(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/top-level-api.html#reactdomserver.rendertostaticmarkup
|
||||
*/
|
||||
function renderToStaticMarkup(element) {
|
||||
invariant(
|
||||
ReactElement.isValidElement(element),
|
||||
|
||||
@@ -60,6 +60,12 @@ var Mixin = {
|
||||
*/
|
||||
destructor: function() {
|
||||
},
|
||||
|
||||
checkpoint: function() {
|
||||
},
|
||||
|
||||
rollback: function() {
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ describe('ReactServerRendering', function() {
|
||||
|
||||
it('should not register event listeners', function() {
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var cb = jest.genMockFn();
|
||||
var cb = jest.fn();
|
||||
|
||||
ReactServerRendering.renderToString(
|
||||
<span onClick={cb}>hello world</span>
|
||||
@@ -303,7 +303,7 @@ describe('ReactServerRendering', function() {
|
||||
|
||||
it('should not register event listeners', function() {
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var cb = jest.genMockFn();
|
||||
var cb = jest.fn();
|
||||
|
||||
ReactServerRendering.renderToString(
|
||||
<span onClick={cb}>hello world</span>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
var CSSProperty = require('CSSProperty');
|
||||
var ExecutionEnvironment = require('ExecutionEnvironment');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var ReactInstrumentation = require('ReactInstrumentation');
|
||||
|
||||
var camelizeStyleName = require('camelizeStyleName');
|
||||
var dangerousStyleValue = require('dangerousStyleValue');
|
||||
@@ -192,6 +192,14 @@ var CSSPropertyOperations = {
|
||||
* @param {ReactDOMComponent} component
|
||||
*/
|
||||
setValueForStyles: function(node, styles, component) {
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onNativeOperation(
|
||||
component._debugID,
|
||||
'update styles',
|
||||
styles
|
||||
);
|
||||
}
|
||||
|
||||
var style = node.style;
|
||||
for (var styleName in styles) {
|
||||
if (!styles.hasOwnProperty(styleName)) {
|
||||
@@ -229,8 +237,4 @@ var CSSPropertyOperations = {
|
||||
|
||||
};
|
||||
|
||||
ReactPerf.measureMethods(CSSPropertyOperations, 'CSSPropertyOperations', {
|
||||
setValueForStyles: 'setValueForStyles',
|
||||
});
|
||||
|
||||
module.exports = CSSPropertyOperations;
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
'use strict';
|
||||
|
||||
var DOMProperty = require('DOMProperty');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
var ReactDOMInstrumentation = require('ReactDOMInstrumentation');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var ReactInstrumentation = require('ReactInstrumentation');
|
||||
|
||||
var quoteAttributeValueForBrowser = require('quoteAttributeValueForBrowser');
|
||||
var warning = require('warning');
|
||||
@@ -134,9 +135,6 @@ var DOMPropertyOperations = {
|
||||
* @param {*} value
|
||||
*/
|
||||
setValueForProperty: function(node, name, value) {
|
||||
if (__DEV__) {
|
||||
ReactDOMInstrumentation.debugTool.onSetValueForProperty(node, name, value);
|
||||
}
|
||||
var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ?
|
||||
DOMProperty.properties[name] : null;
|
||||
if (propertyInfo) {
|
||||
@@ -145,6 +143,7 @@ var DOMPropertyOperations = {
|
||||
mutationMethod(node, value);
|
||||
} else if (shouldIgnoreValue(propertyInfo, value)) {
|
||||
this.deleteValueForProperty(node, name);
|
||||
return;
|
||||
} else if (propertyInfo.mustUseProperty) {
|
||||
var propName = propertyInfo.propertyName;
|
||||
// Must explicitly cast values for HAS_SIDE_EFFECTS-properties to the
|
||||
@@ -171,6 +170,18 @@ var DOMPropertyOperations = {
|
||||
}
|
||||
} else if (DOMProperty.isCustomAttribute(name)) {
|
||||
DOMPropertyOperations.setValueForAttribute(node, name, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
ReactDOMInstrumentation.debugTool.onSetValueForProperty(node, name, value);
|
||||
var payload = {};
|
||||
payload[name] = value;
|
||||
ReactInstrumentation.debugTool.onNativeOperation(
|
||||
ReactDOMComponentTree.getInstanceFromNode(node)._debugID,
|
||||
'update attribute',
|
||||
payload
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -183,6 +194,16 @@ var DOMPropertyOperations = {
|
||||
} else {
|
||||
node.setAttribute(name, '' + value);
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
var payload = {};
|
||||
payload[name] = value;
|
||||
ReactInstrumentation.debugTool.onNativeOperation(
|
||||
ReactDOMComponentTree.getInstanceFromNode(node)._debugID,
|
||||
'update attribute',
|
||||
payload
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -192,9 +213,6 @@ var DOMPropertyOperations = {
|
||||
* @param {string} name
|
||||
*/
|
||||
deleteValueForProperty: function(node, name) {
|
||||
if (__DEV__) {
|
||||
ReactDOMInstrumentation.debugTool.onDeleteValueForProperty(node, name);
|
||||
}
|
||||
var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ?
|
||||
DOMProperty.properties[name] : null;
|
||||
if (propertyInfo) {
|
||||
@@ -218,14 +236,17 @@ var DOMPropertyOperations = {
|
||||
} else if (DOMProperty.isCustomAttribute(name)) {
|
||||
node.removeAttribute(name);
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
ReactDOMInstrumentation.debugTool.onDeleteValueForProperty(node, name);
|
||||
ReactInstrumentation.debugTool.onNativeOperation(
|
||||
ReactDOMComponentTree.getInstanceFromNode(node)._debugID,
|
||||
'remove attribute',
|
||||
name
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
ReactPerf.measureMethods(DOMPropertyOperations, 'DOMPropertyOperations', {
|
||||
setValueForProperty: 'setValueForProperty',
|
||||
setValueForAttribute: 'setValueForAttribute',
|
||||
deleteValueForProperty: 'deleteValueForProperty',
|
||||
});
|
||||
|
||||
module.exports = DOMPropertyOperations;
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
var DOMChildrenOperations = require('DOMChildrenOperations');
|
||||
var ReactDOMIDOperations = require('ReactDOMIDOperations');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
|
||||
/**
|
||||
* Abstracts away all functionality of the reconciler that requires knowledge of
|
||||
@@ -40,12 +39,4 @@ var ReactComponentBrowserEnvironment = {
|
||||
|
||||
};
|
||||
|
||||
ReactPerf.measureMethods(
|
||||
ReactComponentBrowserEnvironment,
|
||||
'ReactComponentBrowserEnvironment',
|
||||
{
|
||||
replaceNodeWithMarkup: 'replaceNodeWithMarkup',
|
||||
}
|
||||
);
|
||||
|
||||
module.exports = ReactComponentBrowserEnvironment;
|
||||
|
||||
@@ -32,9 +32,11 @@ var ReactDOMInput = require('ReactDOMInput');
|
||||
var ReactDOMOption = require('ReactDOMOption');
|
||||
var ReactDOMSelect = require('ReactDOMSelect');
|
||||
var ReactDOMTextarea = require('ReactDOMTextarea');
|
||||
var ReactInstrumentation = require('ReactInstrumentation');
|
||||
var ReactMultiChild = require('ReactMultiChild');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var ReactServerRenderingTransaction = require('ReactServerRenderingTransaction');
|
||||
|
||||
var emptyFunction = require('emptyFunction');
|
||||
var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
|
||||
var invariant = require('invariant');
|
||||
var isEventSupported = require('isEventSupported');
|
||||
@@ -207,6 +209,9 @@ function assertValidProps(component, props) {
|
||||
}
|
||||
|
||||
function enqueuePutListener(inst, registrationName, listener, transaction) {
|
||||
if (transaction instanceof ReactServerRenderingTransaction) {
|
||||
return;
|
||||
}
|
||||
if (__DEV__) {
|
||||
// IE8 has no API for event capturing and the `onScroll` event doesn't
|
||||
// bubble.
|
||||
@@ -218,10 +223,6 @@ function enqueuePutListener(inst, registrationName, listener, transaction) {
|
||||
var containerInfo = inst._nativeContainerInfo;
|
||||
var isDocumentFragment = containerInfo._node && containerInfo._node.nodeType === DOC_FRAGMENT_TYPE;
|
||||
var doc = isDocumentFragment ? containerInfo._node : containerInfo._ownerDocument;
|
||||
if (!doc) {
|
||||
// Server rendering.
|
||||
return;
|
||||
}
|
||||
listenTo(registrationName, doc);
|
||||
transaction.getReactMountReady().enqueue(putListener, {
|
||||
inst: inst,
|
||||
@@ -244,6 +245,19 @@ function optionPostMount() {
|
||||
ReactDOMOption.postMountWrapper(inst);
|
||||
}
|
||||
|
||||
var setContentChildForInstrumentation = emptyFunction;
|
||||
if (__DEV__) {
|
||||
setContentChildForInstrumentation = function(contentToUse) {
|
||||
var debugID = this._debugID;
|
||||
var contentDebugID = debugID + '#text';
|
||||
this._contentDebugID = contentDebugID;
|
||||
ReactInstrumentation.debugTool.onSetDisplayName(contentDebugID, '#text');
|
||||
ReactInstrumentation.debugTool.onSetText(contentDebugID, '' + contentToUse);
|
||||
ReactInstrumentation.debugTool.onMountComponent(contentDebugID);
|
||||
ReactInstrumentation.debugTool.onSetChildren(debugID, [contentDebugID]);
|
||||
};
|
||||
}
|
||||
|
||||
// There are so many media events, it makes sense to just
|
||||
// maintain a list rather than create a `trapBubbledEvent` for each
|
||||
var mediaEvents = {
|
||||
@@ -447,6 +461,7 @@ function ReactDOMComponent(element) {
|
||||
this._flags = 0;
|
||||
if (__DEV__) {
|
||||
this._ancestorInfo = null;
|
||||
this._contentDebugID = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -569,7 +584,7 @@ ReactDOMComponent.Mixin = {
|
||||
div.innerHTML = `<${type}></${type}>`;
|
||||
el = div.removeChild(div.firstChild);
|
||||
} else {
|
||||
el = ownerDocument.createElement(this._currentElement.type);
|
||||
el = ownerDocument.createElement(this._currentElement.type, props.is || null);
|
||||
}
|
||||
} else {
|
||||
el = ownerDocument.createElementNS(
|
||||
@@ -710,6 +725,9 @@ ReactDOMComponent.Mixin = {
|
||||
if (contentToUse != null) {
|
||||
// TODO: Validate that text is allowed as a child of this node
|
||||
ret = escapeTextContentForBrowser(contentToUse);
|
||||
if (__DEV__) {
|
||||
setContentChildForInstrumentation.call(this, contentToUse);
|
||||
}
|
||||
} else if (childrenToUse != null) {
|
||||
var mountImages = this.mountChildren(
|
||||
childrenToUse,
|
||||
@@ -749,6 +767,9 @@ ReactDOMComponent.Mixin = {
|
||||
var childrenToUse = contentToUse != null ? null : props.children;
|
||||
if (contentToUse != null) {
|
||||
// TODO: Validate that text is allowed as a child of this node
|
||||
if (__DEV__) {
|
||||
setContentChildForInstrumentation.call(this, contentToUse);
|
||||
}
|
||||
DOMLazyTree.queueText(lazyTree, contentToUse);
|
||||
} else if (childrenToUse != null) {
|
||||
var mountImages = this.mountChildren(
|
||||
@@ -996,17 +1017,34 @@ ReactDOMComponent.Mixin = {
|
||||
this.updateChildren(null, transaction, context);
|
||||
} else if (lastHasContentOrHtml && !nextHasContentOrHtml) {
|
||||
this.updateTextContent('');
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onSetChildren(this._debugID, []);
|
||||
}
|
||||
}
|
||||
|
||||
if (nextContent != null) {
|
||||
if (lastContent !== nextContent) {
|
||||
this.updateTextContent('' + nextContent);
|
||||
if (__DEV__) {
|
||||
this._contentDebugID = this._debugID + '#text';
|
||||
setContentChildForInstrumentation.call(this, nextContent);
|
||||
}
|
||||
}
|
||||
} else if (nextHtml != null) {
|
||||
if (lastHtml !== nextHtml) {
|
||||
this.updateMarkup('' + nextHtml);
|
||||
}
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onSetChildren(this._debugID, []);
|
||||
}
|
||||
} else if (nextChildren != null) {
|
||||
if (__DEV__) {
|
||||
if (this._contentDebugID) {
|
||||
ReactInstrumentation.debugTool.onUnmountComponent(this._contentDebugID);
|
||||
this._contentDebugID = null;
|
||||
}
|
||||
}
|
||||
|
||||
this.updateChildren(nextChildren, transaction, context);
|
||||
}
|
||||
},
|
||||
@@ -1064,6 +1102,13 @@ ReactDOMComponent.Mixin = {
|
||||
this._rootNodeID = null;
|
||||
this._domID = null;
|
||||
this._wrapperState = null;
|
||||
|
||||
if (__DEV__) {
|
||||
if (this._contentDebugID) {
|
||||
ReactInstrumentation.debugTool.onUnmountComponent(this._contentDebugID);
|
||||
this._contentDebugID = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getPublicInstance: function() {
|
||||
@@ -1072,11 +1117,6 @@ ReactDOMComponent.Mixin = {
|
||||
|
||||
};
|
||||
|
||||
ReactPerf.measureMethods(ReactDOMComponent.Mixin, 'ReactDOMComponent', {
|
||||
mountComponent: 'mountComponent',
|
||||
receiveComponent: 'receiveComponent',
|
||||
});
|
||||
|
||||
Object.assign(
|
||||
ReactDOMComponent.prototype,
|
||||
ReactDOMComponent.Mixin,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
var DOMChildrenOperations = require('DOMChildrenOperations');
|
||||
var DOMLazyTree = require('DOMLazyTree');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var ReactInstrumentation = require('ReactInstrumentation');
|
||||
|
||||
var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
|
||||
var invariant = require('invariant');
|
||||
@@ -67,6 +67,8 @@ Object.assign(ReactDOMTextComponent.prototype, {
|
||||
context
|
||||
) {
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onSetText(this._debugID, this._stringText);
|
||||
|
||||
var parentInfo;
|
||||
if (nativeParent != null) {
|
||||
parentInfo = nativeParent._ancestorInfo;
|
||||
@@ -140,6 +142,13 @@ Object.assign(ReactDOMTextComponent.prototype, {
|
||||
commentNodes[1],
|
||||
nextStringText
|
||||
);
|
||||
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onSetText(
|
||||
this._debugID,
|
||||
nextStringText
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -178,13 +187,4 @@ Object.assign(ReactDOMTextComponent.prototype, {
|
||||
|
||||
});
|
||||
|
||||
ReactPerf.measureMethods(
|
||||
ReactDOMTextComponent.prototype,
|
||||
'ReactDOMTextComponent',
|
||||
{
|
||||
mountComponent: 'mountComponent',
|
||||
receiveComponent: 'receiveComponent',
|
||||
}
|
||||
);
|
||||
|
||||
module.exports = ReactDOMTextComponent;
|
||||
|
||||
@@ -15,7 +15,6 @@ var BeforeInputEventPlugin = require('BeforeInputEventPlugin');
|
||||
var ChangeEventPlugin = require('ChangeEventPlugin');
|
||||
var DefaultEventPluginOrder = require('DefaultEventPluginOrder');
|
||||
var EnterLeaveEventPlugin = require('EnterLeaveEventPlugin');
|
||||
var ExecutionEnvironment = require('ExecutionEnvironment');
|
||||
var HTMLDOMPropertyConfig = require('HTMLDOMPropertyConfig');
|
||||
var ReactComponentBrowserEnvironment =
|
||||
require('ReactComponentBrowserEnvironment');
|
||||
@@ -91,14 +90,6 @@ function inject() {
|
||||
);
|
||||
|
||||
ReactInjection.Component.injectEnvironment(ReactComponentBrowserEnvironment);
|
||||
|
||||
if (__DEV__) {
|
||||
var url = (ExecutionEnvironment.canUseDOM && window.location.href) || '';
|
||||
if ((/[?&]react_perf\b/).test(url)) {
|
||||
var ReactDefaultPerf = require('ReactDefaultPerf');
|
||||
ReactDefaultPerf.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -19,7 +19,6 @@ var ReactClass = require('ReactClass');
|
||||
var ReactEmptyComponent = require('ReactEmptyComponent');
|
||||
var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
|
||||
var ReactNativeComponent = require('ReactNativeComponent');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
|
||||
var ReactInjection = {
|
||||
@@ -31,7 +30,6 @@ var ReactInjection = {
|
||||
EventPluginUtils: EventPluginUtils.injection,
|
||||
EventEmitter: ReactBrowserEventEmitter.injection,
|
||||
NativeComponent: ReactNativeComponent.injection,
|
||||
Perf: ReactPerf.injection,
|
||||
Updates: ReactUpdates.injection,
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
describe('DOMPropertyOperations', function() {
|
||||
var DOMPropertyOperations;
|
||||
var DOMProperty;
|
||||
var ReactDOMComponentTree;
|
||||
|
||||
beforeEach(function() {
|
||||
jest.resetModuleRegistry();
|
||||
@@ -22,6 +23,7 @@ describe('DOMPropertyOperations', function() {
|
||||
|
||||
DOMPropertyOperations = require('DOMPropertyOperations');
|
||||
DOMProperty = require('DOMProperty');
|
||||
ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
});
|
||||
|
||||
describe('createMarkupForProperty', function() {
|
||||
@@ -172,9 +174,12 @@ describe('DOMPropertyOperations', function() {
|
||||
|
||||
describe('setValueForProperty', function() {
|
||||
var stubNode;
|
||||
var stubInstance;
|
||||
|
||||
beforeEach(function() {
|
||||
stubNode = document.createElement('div');
|
||||
stubInstance = {_debugID: 1};
|
||||
ReactDOMComponentTree.precacheNode(stubInstance, stubNode);
|
||||
});
|
||||
|
||||
it('should set values as properties by default', function() {
|
||||
@@ -223,6 +228,8 @@ describe('DOMPropertyOperations', function() {
|
||||
|
||||
it('should not remove empty attributes for special properties', function() {
|
||||
stubNode = document.createElement('input');
|
||||
ReactDOMComponentTree.precacheNode(stubInstance, stubNode);
|
||||
|
||||
DOMPropertyOperations.setValueForProperty(stubNode, 'value', '');
|
||||
// JSDOM does not behave correctly for attributes/properties
|
||||
//expect(stubNode.getAttribute('value')).toBe('');
|
||||
@@ -254,7 +261,7 @@ describe('DOMPropertyOperations', function() {
|
||||
});
|
||||
|
||||
it('should use mutation method where applicable', function() {
|
||||
var foobarSetter = jest.genMockFn();
|
||||
var foobarSetter = jest.fn();
|
||||
// inject foobar DOM property
|
||||
DOMProperty.injection.injectDOMPropertyConfig({
|
||||
Properties: {foobar: null},
|
||||
@@ -343,9 +350,12 @@ describe('DOMPropertyOperations', function() {
|
||||
|
||||
describe('deleteValueForProperty', function() {
|
||||
var stubNode;
|
||||
var stubInstance;
|
||||
|
||||
beforeEach(function() {
|
||||
stubNode = document.createElement('div');
|
||||
stubInstance = {_debugID: 1};
|
||||
ReactDOMComponentTree.precacheNode(stubInstance, stubNode);
|
||||
});
|
||||
|
||||
it('should remove attributes for normal properties', function() {
|
||||
@@ -361,6 +371,8 @@ describe('DOMPropertyOperations', function() {
|
||||
|
||||
it('should not remove attributes for special properties', function() {
|
||||
stubNode = document.createElement('input');
|
||||
ReactDOMComponentTree.precacheNode(stubInstance, stubNode);
|
||||
|
||||
stubNode.setAttribute('value', 'foo');
|
||||
|
||||
DOMPropertyOperations.deleteValueForProperty(stubNode, 'value');
|
||||
@@ -371,6 +383,8 @@ describe('DOMPropertyOperations', function() {
|
||||
|
||||
it('should not leave all options selected when deleting multiple', function() {
|
||||
stubNode = document.createElement('select');
|
||||
ReactDOMComponentTree.precacheNode(stubInstance, stubNode);
|
||||
|
||||
stubNode.multiple = true;
|
||||
stubNode.appendChild(document.createElement('option'));
|
||||
stubNode.appendChild(document.createElement('option'));
|
||||
|
||||
@@ -15,12 +15,14 @@
|
||||
describe('ReactDOMComponent', function() {
|
||||
var React;
|
||||
var ReactDOM;
|
||||
var ReactDOMFeatureFlags;
|
||||
var ReactDOMServer;
|
||||
|
||||
beforeEach(function() {
|
||||
jest.resetModuleRegistry();
|
||||
React = require('React');
|
||||
ReactDOM = require('ReactDOM');
|
||||
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
|
||||
ReactDOMServer = require('ReactDOMServer');
|
||||
});
|
||||
|
||||
@@ -448,11 +450,11 @@ describe('ReactDOMComponent', function() {
|
||||
|
||||
var node = container.firstChild;
|
||||
var nodeSetAttribute = node.setAttribute;
|
||||
node.setAttribute = jest.genMockFn();
|
||||
node.setAttribute = jest.fn();
|
||||
node.setAttribute.mockImpl(nodeSetAttribute);
|
||||
|
||||
var nodeRemoveAttribute = node.removeAttribute;
|
||||
node.removeAttribute = jest.genMockFn();
|
||||
node.removeAttribute = jest.fn();
|
||||
node.removeAttribute.mockImpl(nodeRemoveAttribute);
|
||||
|
||||
ReactDOM.render(<div id="" />, container);
|
||||
@@ -486,7 +488,7 @@ describe('ReactDOMComponent', function() {
|
||||
|
||||
var node = container.firstChild;
|
||||
var nodeValue = ''; // node.value always returns undefined
|
||||
var nodeValueSetter = jest.genMockFn();
|
||||
var nodeValueSetter = jest.fn();
|
||||
Object.defineProperty(node, 'value', {
|
||||
get: function() {
|
||||
return nodeValue;
|
||||
@@ -517,7 +519,7 @@ describe('ReactDOMComponent', function() {
|
||||
|
||||
var node = container.firstChild;
|
||||
var nodeValue = true;
|
||||
var nodeValueSetter = jest.genMockFn();
|
||||
var nodeValueSetter = jest.fn();
|
||||
Object.defineProperty(node, 'checked', {
|
||||
get: function() {
|
||||
return nodeValue;
|
||||
@@ -550,7 +552,7 @@ describe('ReactDOMComponent', function() {
|
||||
var container = document.createElement('div');
|
||||
var node = ReactDOM.render(<div />, container);
|
||||
|
||||
var setter = jest.genMockFn();
|
||||
var setter = jest.fn();
|
||||
node.setAttribute = setter;
|
||||
|
||||
ReactDOM.render(<div dir={null} />, container);
|
||||
@@ -819,8 +821,8 @@ describe('ReactDOMComponent', function() {
|
||||
it('should execute custom event plugin listening behavior', function() {
|
||||
var SimpleEventPlugin = require('SimpleEventPlugin');
|
||||
|
||||
SimpleEventPlugin.didPutListener = jest.genMockFn();
|
||||
SimpleEventPlugin.willDeleteListener = jest.genMockFn();
|
||||
SimpleEventPlugin.didPutListener = jest.fn();
|
||||
SimpleEventPlugin.willDeleteListener = jest.fn();
|
||||
|
||||
var container = document.createElement('div');
|
||||
ReactDOM.render(
|
||||
@@ -838,8 +840,8 @@ describe('ReactDOMComponent', function() {
|
||||
it('should handle null and missing properly with event hooks', function() {
|
||||
var SimpleEventPlugin = require('SimpleEventPlugin');
|
||||
|
||||
SimpleEventPlugin.didPutListener = jest.genMockFn();
|
||||
SimpleEventPlugin.willDeleteListener = jest.genMockFn();
|
||||
SimpleEventPlugin.didPutListener = jest.fn();
|
||||
SimpleEventPlugin.willDeleteListener = jest.fn();
|
||||
var container = document.createElement('div');
|
||||
|
||||
ReactDOM.render(<div onClick={false} />, container);
|
||||
@@ -886,6 +888,17 @@ describe('ReactDOMComponent', function() {
|
||||
'or use `props.dangerouslySetInnerHTML`. Check the render method of X.'
|
||||
);
|
||||
});
|
||||
|
||||
it('should support custom elements which extend native elements', function() {
|
||||
if (ReactDOMFeatureFlags.useCreateElement) {
|
||||
var container = document.createElement('div');
|
||||
spyOn(document, 'createElement').andCallThrough();
|
||||
ReactDOM.render(<div is="custom-div" />, container);
|
||||
expect(document.createElement).toHaveBeenCalledWith('div', 'custom-div');
|
||||
} else {
|
||||
expect(ReactDOMServer.renderToString(<div is="custom-div" />)).toContain('is="custom-div"');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateComponent', function() {
|
||||
@@ -1050,6 +1063,12 @@ describe('ReactDOMComponent', function() {
|
||||
'Warning: This browser doesn\'t support the `onScroll` event'
|
||||
);
|
||||
});
|
||||
|
||||
it('should not warn when server-side rendering `onScroll`', function() {
|
||||
spyOn(console, 'error');
|
||||
ReactDOMServer.renderToString(<div onScroll={() => {}}/>);
|
||||
expect(console.error).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('tag sanitization', function() {
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Noop
|
||||
|
||||
// TODO: Move all initialization callers back into react-native
|
||||
@@ -1,16 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Mock of the Native Hooks
|
||||
|
||||
var Platform = {};
|
||||
|
||||
module.exports = Platform;
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Noop
|
||||
|
||||
// TODO: Move all initialization callers back into react-native
|
||||
+6
-3
@@ -9,8 +9,11 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
// TODO: Figure out a way to drop this dependency
|
||||
var createReactNativeComponentClass = require('createReactNativeComponentClass');
|
||||
|
||||
var InteractionManager = {};
|
||||
var View = createReactNativeComponentClass({
|
||||
validAttributes: {},
|
||||
uiViewClassName: 'View',
|
||||
});
|
||||
|
||||
module.exports = InteractionManager;
|
||||
module.exports = View;
|
||||
@@ -1,18 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// TODO: Replace all callers with spread
|
||||
|
||||
var merge = function(a, b) {
|
||||
return {...a, ...b};
|
||||
};
|
||||
|
||||
module.exports = merge;
|
||||
+7
-36
@@ -11,7 +11,6 @@
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var Platform = require('Platform');
|
||||
var ReactNativePropRegistry = require('ReactNativePropRegistry');
|
||||
|
||||
var deepDiffer = require('deepDiffer');
|
||||
@@ -47,21 +46,6 @@ type NestedNode = Array<NestedNode> | Object | number;
|
||||
var removedKeys = null;
|
||||
var removedKeyCount = 0;
|
||||
|
||||
function translateKey(propKey: string) : string {
|
||||
if (propKey === 'transform') {
|
||||
// We currently special case the key for `transform`. iOS uses the
|
||||
// transformMatrix name and Android uses the decomposedMatrix name.
|
||||
// TODO: We could unify these names and just use the name `transform`
|
||||
// all the time. Just need to update the native side.
|
||||
if (Platform.OS === 'android') {
|
||||
return 'decomposedMatrix';
|
||||
} else {
|
||||
return 'transformMatrix';
|
||||
}
|
||||
}
|
||||
return propKey;
|
||||
}
|
||||
|
||||
function defaultDiffer(prevProp: mixed, nextProp: mixed) : boolean {
|
||||
if (typeof nextProp !== 'object' || nextProp === null) {
|
||||
// Scalars have already been checked for equality
|
||||
@@ -326,7 +310,6 @@ function diffProperties(
|
||||
var attributeConfig : ?(CustomAttributeConfiguration | AttributeConfiguration);
|
||||
var nextProp;
|
||||
var prevProp;
|
||||
var altKey;
|
||||
|
||||
for (var propKey in nextProps) {
|
||||
attributeConfig = validAttributes[propKey];
|
||||
@@ -334,12 +317,6 @@ function diffProperties(
|
||||
continue; // not a valid native prop
|
||||
}
|
||||
|
||||
altKey = translateKey(propKey);
|
||||
if (!validAttributes[altKey]) {
|
||||
// If there is no config for the alternative, bail out. Helps ART.
|
||||
altKey = propKey;
|
||||
}
|
||||
|
||||
prevProp = prevProps[propKey];
|
||||
nextProp = nextProps[propKey];
|
||||
|
||||
@@ -367,7 +344,7 @@ function diffProperties(
|
||||
removedKeys[propKey] = false;
|
||||
}
|
||||
|
||||
if (updatePayload && updatePayload[altKey] !== undefined) {
|
||||
if (updatePayload && updatePayload[propKey] !== undefined) {
|
||||
// Something else already triggered an update to this key because another
|
||||
// value diffed. Since we're now later in the nested arrays our value is
|
||||
// more important so we need to calculate it and override the existing
|
||||
@@ -376,14 +353,14 @@ function diffProperties(
|
||||
// Pattern match on: attributeConfig
|
||||
if (typeof attributeConfig !== 'object') {
|
||||
// case: !Object is the default case
|
||||
updatePayload[altKey] = nextProp;
|
||||
updatePayload[propKey] = nextProp;
|
||||
} else if (typeof attributeConfig.diff === 'function' ||
|
||||
typeof attributeConfig.process === 'function') {
|
||||
// case: CustomAttributeConfiguration
|
||||
var nextValue = typeof attributeConfig.process === 'function' ?
|
||||
attributeConfig.process(nextProp) :
|
||||
nextProp;
|
||||
updatePayload[altKey] = nextValue;
|
||||
updatePayload[propKey] = nextValue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -397,7 +374,7 @@ function diffProperties(
|
||||
// case: !Object is the default case
|
||||
if (defaultDiffer(prevProp, nextProp)) {
|
||||
// a normal leaf has changed
|
||||
(updatePayload || (updatePayload = {}))[altKey] = nextProp;
|
||||
(updatePayload || (updatePayload = {}))[propKey] = nextProp;
|
||||
}
|
||||
} else if (typeof attributeConfig.diff === 'function' ||
|
||||
typeof attributeConfig.process === 'function') {
|
||||
@@ -411,7 +388,7 @@ function diffProperties(
|
||||
nextValue = typeof attributeConfig.process === 'function' ?
|
||||
attributeConfig.process(nextProp) :
|
||||
nextProp;
|
||||
(updatePayload || (updatePayload = {}))[altKey] = nextValue;
|
||||
(updatePayload || (updatePayload = {}))[propKey] = nextValue;
|
||||
}
|
||||
} else {
|
||||
// default: fallthrough case when nested properties are defined
|
||||
@@ -446,13 +423,7 @@ function diffProperties(
|
||||
continue; // not a valid native prop
|
||||
}
|
||||
|
||||
altKey = translateKey(propKey);
|
||||
if (!attributeConfig[altKey]) {
|
||||
// If there is no config for the alternative, bail out. Helps ART.
|
||||
altKey = propKey;
|
||||
}
|
||||
|
||||
if (updatePayload && updatePayload[altKey] !== undefined) {
|
||||
if (updatePayload && updatePayload[propKey] !== undefined) {
|
||||
// This was already updated to a diff result earlier.
|
||||
continue;
|
||||
}
|
||||
@@ -468,7 +439,7 @@ function diffProperties(
|
||||
|
||||
// case: CustomAttributeConfiguration | !Object
|
||||
// Flag the leaf property for removal by sending a sentinel.
|
||||
(updatePayload || (updatePayload = {}))[altKey] = null;
|
||||
(updatePayload || (updatePayload = {}))[propKey] = null;
|
||||
if (!removedKeys) {
|
||||
removedKeys = {};
|
||||
}
|
||||
+4
-5
@@ -6,7 +6,7 @@
|
||||
* 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 IOSNativeBridgeEventPlugin
|
||||
* @providesModule ReactNativeBridgeEventPlugin
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
@@ -15,7 +15,6 @@ var EventPropagators = require('EventPropagators');
|
||||
var SyntheticEvent = require('SyntheticEvent');
|
||||
var UIManager = require('UIManager');
|
||||
|
||||
var merge = require('merge');
|
||||
var warning = require('warning');
|
||||
|
||||
var customBubblingEventTypes = UIManager.customBubblingEventTypes;
|
||||
@@ -36,9 +35,9 @@ for (var directTypeName in customDirectEventTypes) {
|
||||
allTypesByEventName[directTypeName] = customDirectEventTypes[directTypeName];
|
||||
}
|
||||
|
||||
var IOSNativeBridgeEventPlugin = {
|
||||
var ReactNativeBridgeEventPlugin = {
|
||||
|
||||
eventTypes: merge(customBubblingEventTypes, customDirectEventTypes),
|
||||
eventTypes: { ...customBubblingEventTypes, ...customDirectEventTypes },
|
||||
|
||||
/**
|
||||
* @see {EventPluginHub.extractEvents}
|
||||
@@ -68,4 +67,4 @@ var IOSNativeBridgeEventPlugin = {
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = IOSNativeBridgeEventPlugin;
|
||||
module.exports = ReactNativeBridgeEventPlugin;
|
||||
+5
-15
@@ -13,7 +13,6 @@
|
||||
|
||||
var ReactNativeComponentTree = require('ReactNativeComponentTree');
|
||||
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var UIManager = require('UIManager');
|
||||
|
||||
/**
|
||||
@@ -71,12 +70,7 @@ var dangerouslyProcessChildrenUpdates = function(inst, childrenUpdates) {
|
||||
* `ReactComponent.DOMIDOperations`.
|
||||
*/
|
||||
var ReactNativeDOMIDOperations = {
|
||||
dangerouslyProcessChildrenUpdates: ReactPerf.measure(
|
||||
// FIXME(frantic): #4441289 Hack to avoid modifying react-tools
|
||||
'ReactDOMIDOperations',
|
||||
'dangerouslyProcessChildrenUpdates',
|
||||
dangerouslyProcessChildrenUpdates
|
||||
),
|
||||
dangerouslyProcessChildrenUpdates,
|
||||
|
||||
/**
|
||||
* Replaces a view that exists in the document with markup.
|
||||
@@ -84,14 +78,10 @@ var ReactNativeDOMIDOperations = {
|
||||
* @param {string} id ID of child to be replaced.
|
||||
* @param {string} markup Mount image to replace child with id.
|
||||
*/
|
||||
dangerouslyReplaceNodeWithMarkupByID: ReactPerf.measure(
|
||||
'ReactDOMIDOperations',
|
||||
'dangerouslyReplaceNodeWithMarkupByID',
|
||||
function(id, mountImage) {
|
||||
var oldTag = id;
|
||||
UIManager.replaceExistingNonRootView(oldTag, mountImage);
|
||||
}
|
||||
),
|
||||
dangerouslyReplaceNodeWithMarkupByID: function(id, mountImage) {
|
||||
var oldTag = id;
|
||||
UIManager.replaceExistingNonRootView(oldTag, mountImage);
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = ReactNativeDOMIDOperations;
|
||||
+15
-12
@@ -15,39 +15,42 @@
|
||||
* Make sure essential globals are available and are patched correctly. Please don't remove this
|
||||
* line. Bundles created by react-packager `require` it before executing any application code. This
|
||||
* ensures it exists in the dependency graph and can be `require`d.
|
||||
* TODO: require this in packager, not in React #10932517
|
||||
*/
|
||||
require('InitializeJavaScriptAppEngine');
|
||||
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var EventPluginUtils = require('EventPluginUtils');
|
||||
var IOSDefaultEventPluginOrder = require('IOSDefaultEventPluginOrder');
|
||||
var IOSNativeBridgeEventPlugin = require('IOSNativeBridgeEventPlugin');
|
||||
var ReactElement = require('ReactElement');
|
||||
var RCTEventEmitter = require('RCTEventEmitter');
|
||||
var ReactComponentEnvironment = require('ReactComponentEnvironment');
|
||||
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactEmptyComponent = require('ReactEmptyComponent');
|
||||
var ReactNativeBridgeEventPlugin = require('ReactNativeBridgeEventPlugin');
|
||||
var ReactNativeComponent = require('ReactNativeComponent');
|
||||
var ReactNativeComponentEnvironment = require('ReactNativeComponentEnvironment');
|
||||
var ReactNativeComponentTree = require('ReactNativeComponentTree');
|
||||
var ReactNativeEventEmitter = require('ReactNativeEventEmitter');
|
||||
var ReactNativeEventPluginOrder = require('ReactNativeEventPluginOrder');
|
||||
var ReactNativeGlobalResponderHandler = require('ReactNativeGlobalResponderHandler');
|
||||
var ReactNativeTextComponent = require('ReactNativeTextComponent');
|
||||
var ReactNativeTreeTraversal = require('ReactNativeTreeTraversal');
|
||||
var ReactNativeComponent = require('ReactNativeComponent');
|
||||
var ReactNativeComponentTree = require('ReactNativeComponentTree');
|
||||
var ReactSimpleEmptyComponent = require('ReactSimpleEmptyComponent');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
var ResponderEventPlugin = require('ResponderEventPlugin');
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
// Just to ensure this gets packaged, since its only caller is from Native.
|
||||
require('RCTEventEmitter');
|
||||
require('RCTLog');
|
||||
require('JSTimersExecution');
|
||||
|
||||
function inject() {
|
||||
/**
|
||||
* Register the event emitter with the native bridge
|
||||
*/
|
||||
RCTEventEmitter.register(ReactNativeEventEmitter);
|
||||
|
||||
/**
|
||||
* Inject module for resolving DOM hierarchy and plugin ordering.
|
||||
*/
|
||||
EventPluginHub.injection.injectEventPluginOrder(IOSDefaultEventPluginOrder);
|
||||
EventPluginHub.injection.injectEventPluginOrder(ReactNativeEventPluginOrder);
|
||||
EventPluginUtils.injection.injectComponentTree(ReactNativeComponentTree);
|
||||
EventPluginUtils.injection.injectTreeTraversal(ReactNativeTreeTraversal);
|
||||
|
||||
@@ -61,7 +64,7 @@ function inject() {
|
||||
*/
|
||||
EventPluginHub.injection.injectEventPluginsByName({
|
||||
'ResponderEventPlugin': ResponderEventPlugin,
|
||||
'IOSNativeBridgeEventPlugin': IOSNativeBridgeEventPlugin,
|
||||
'ReactNativeBridgeEventPlugin': ReactNativeBridgeEventPlugin,
|
||||
});
|
||||
|
||||
ReactUpdates.injection.injectReconcileTransaction(
|
||||
+5
-4
@@ -11,15 +11,14 @@
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var EventConstants = require('EventConstants');
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var EventPluginRegistry = require('EventPluginRegistry');
|
||||
var ReactEventEmitterMixin = require('ReactEventEmitterMixin');
|
||||
var ReactNativeComponentTree = require('ReactNativeComponentTree');
|
||||
var ReactNativeTagHandles = require('ReactNativeTagHandles');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
var EventConstants = require('EventConstants');
|
||||
|
||||
var merge = require('merge');
|
||||
var warning = require('warning');
|
||||
|
||||
var topLevelTypes = EventConstants.topLevelTypes;
|
||||
@@ -91,7 +90,9 @@ var removeTouchesAtIndices = function(
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
var ReactNativeEventEmitter = merge(ReactEventEmitterMixin, {
|
||||
var ReactNativeEventEmitter = {
|
||||
|
||||
...ReactEventEmitterMixin,
|
||||
|
||||
registrationNames: EventPluginRegistry.registrationNameModules,
|
||||
|
||||
@@ -218,6 +219,6 @@ var ReactNativeEventEmitter = merge(ReactEventEmitterMixin, {
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = ReactNativeEventEmitter;
|
||||
+4
-4
@@ -6,14 +6,14 @@
|
||||
* 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 IOSDefaultEventPluginOrder
|
||||
* @providesModule ReactNativeEventPluginOrder
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var IOSDefaultEventPluginOrder = [
|
||||
var ReactNativeEventPluginOrder = [
|
||||
'ResponderEventPlugin',
|
||||
'IOSNativeBridgeEventPlugin',
|
||||
'ReactNativeBridgeEventPlugin',
|
||||
];
|
||||
|
||||
module.exports = IOSDefaultEventPluginOrder;
|
||||
module.exports = ReactNativeEventPluginOrder;
|
||||
+33
-21
@@ -12,9 +12,9 @@
|
||||
'use strict';
|
||||
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactInstrumentation = require('ReactInstrumentation');
|
||||
var ReactNativeContainerInfo = require('ReactNativeContainerInfo');
|
||||
var ReactNativeTagHandles = require('ReactNativeTagHandles');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var ReactReconciler = require('ReactReconciler');
|
||||
var ReactUpdateQueue = require('ReactUpdateQueue');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
@@ -138,6 +138,16 @@ var ReactNativeMount = {
|
||||
var instance = instantiateReactComponent(nextWrappedElement);
|
||||
ReactNativeMount._instancesByContainerID[containerTag] = instance;
|
||||
|
||||
if (__DEV__) {
|
||||
// Mute future events from the top level wrapper.
|
||||
// It is an implementation detail that devtools should not know about.
|
||||
instance._debugID = 0;
|
||||
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onBeginFlush();
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -147,6 +157,13 @@ var ReactNativeMount = {
|
||||
instance,
|
||||
containerTag
|
||||
);
|
||||
if (__DEV__) {
|
||||
// The instance here is TopLevelWrapper so we report mount for its child.
|
||||
ReactInstrumentation.debugTool.onMountRootComponent(
|
||||
instance._renderedComponent._debugID
|
||||
);
|
||||
ReactInstrumentation.debugTool.onEndFlush();
|
||||
}
|
||||
var component = instance.getPublicInstance();
|
||||
if (callback) {
|
||||
callback.call(component);
|
||||
@@ -158,20 +175,15 @@ var ReactNativeMount = {
|
||||
* @param {View} view View tree image.
|
||||
* @param {number} containerViewID View to insert sub-view into.
|
||||
*/
|
||||
_mountImageIntoNode: ReactPerf.measure(
|
||||
// FIXME(frantic): #4441289 Hack to avoid modifying react-tools
|
||||
'ReactComponentBrowserEnvironment',
|
||||
'mountImageIntoNode',
|
||||
function(mountImage, containerID) {
|
||||
// Since we now know that the `mountImage` has been mounted, we can
|
||||
// mark it as such.
|
||||
var childTag = mountImage;
|
||||
UIManager.setChildren(
|
||||
containerID,
|
||||
[childTag]
|
||||
);
|
||||
}
|
||||
),
|
||||
_mountImageIntoNode: function(mountImage, containerID) {
|
||||
// Since we now know that the `mountImage` has been mounted, we can
|
||||
// mark it as such.
|
||||
var childTag = mountImage;
|
||||
UIManager.setChildren(
|
||||
containerID,
|
||||
[childTag]
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Standard unmounting of the component that is rendered into `containerID`,
|
||||
@@ -204,8 +216,14 @@ var ReactNativeMount = {
|
||||
if (!instance) {
|
||||
return false;
|
||||
}
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onBeginFlush();
|
||||
}
|
||||
ReactNativeMount.unmountComponentFromNode(instance, containerTag);
|
||||
delete ReactNativeMount._instancesByContainerID[containerTag];
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onEndFlush();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
@@ -229,10 +247,4 @@ var ReactNativeMount = {
|
||||
|
||||
};
|
||||
|
||||
ReactNativeMount.renderComponent = ReactPerf.measure(
|
||||
'ReactMount',
|
||||
'_renderNewRootComponent',
|
||||
ReactNativeMount.renderComponent
|
||||
);
|
||||
|
||||
module.exports = ReactNativeMount;
|
||||
+11
@@ -11,6 +11,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var ReactInstrumentation = require('ReactInstrumentation');
|
||||
var ReactNativeComponentTree = require('ReactNativeComponentTree');
|
||||
var ReactNativeTagHandles = require('ReactNativeTagHandles');
|
||||
var UIManager = require('UIManager');
|
||||
@@ -28,6 +29,10 @@ var ReactNativeTextComponent = function(text) {
|
||||
Object.assign(ReactNativeTextComponent.prototype, {
|
||||
|
||||
mountComponent: function(transaction, nativeParent, nativeContainerInfo, context) {
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onSetText(this._debugID, this._stringText);
|
||||
}
|
||||
|
||||
// TODO: nativeParent should have this context already. Stop abusing context.
|
||||
invariant(
|
||||
context.isInAParentText,
|
||||
@@ -65,6 +70,12 @@ Object.assign(ReactNativeTextComponent.prototype, {
|
||||
'RCTRawText',
|
||||
{text: this._stringText}
|
||||
);
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onSetText(
|
||||
this._debugID,
|
||||
nextStringText
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
+1
-1
@@ -11,4 +11,4 @@
|
||||
|
||||
// Noop
|
||||
|
||||
// TODO: Move all initialization callers back into react-native
|
||||
// TODO #10932517: Move all initialization callers back into react-native
|
||||
+4
-2
@@ -9,6 +9,8 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
// Noop
|
||||
var RCTEventEmitter = {
|
||||
register: jest.fn(),
|
||||
};
|
||||
|
||||
// TODO: Move all initialization callers back into react-native
|
||||
module.exports = RCTEventEmitter;
|
||||
+6
-4
@@ -12,10 +12,12 @@
|
||||
// Mock of the Native Hooks
|
||||
|
||||
var RCTUIManager = {
|
||||
createView: jest.genMockFunction(),
|
||||
setChildren: jest.genMockFunction(),
|
||||
manageChildren: jest.genMockFunction(),
|
||||
updateView: jest.genMockFunction(),
|
||||
createView: jest.fn(),
|
||||
setChildren: jest.fn(),
|
||||
manageChildren: jest.fn(),
|
||||
updateView: jest.fn(),
|
||||
removeSubviewsFromContainerWithID: jest.fn(),
|
||||
replaceExistingNonRootView: jest.fn(),
|
||||
};
|
||||
|
||||
module.exports = RCTUIManager;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user