mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
Merge pull request #6753 from facebook/fix-6750
Fix a memory leak in ReactComponentTreeDevtool
This commit is contained in:
@@ -48,45 +48,54 @@ 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__) {
|
||||
if (!isProfiling || currentFlushNesting === 0) {
|
||||
currentFlushStartTime = null;
|
||||
currentFlushMeasurements = null;
|
||||
return;
|
||||
}
|
||||
|
||||
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: 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;
|
||||
}, {}),
|
||||
treeSnapshot: getTreeSnapshot(registeredIDs),
|
||||
});
|
||||
}
|
||||
|
||||
clearHistory();
|
||||
currentFlushStartTime = performanceNow();
|
||||
currentFlushMeasurements = [];
|
||||
ReactComponentTreeDevtool.purgeUnmountedComponents();
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -241,6 +241,21 @@ describe('ReactPerf', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should include stats for components unmounted during measurement', function() {
|
||||
var container = document.createElement('div');
|
||||
var measurements = measure(() => {
|
||||
ReactDOM.render(<Div><Div key="a" /></Div>, container);
|
||||
ReactDOM.render(<Div><Div key="b" /></Div>, container);
|
||||
});
|
||||
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');
|
||||
|
||||
@@ -106,6 +106,11 @@ var ReactComponentTreeDevtool = {
|
||||
},
|
||||
|
||||
purgeUnmountedComponents() {
|
||||
if (ReactComponentTreeDevtool._preventPurging) {
|
||||
// Should only be used for testing.
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(tree)
|
||||
.filter(id => !tree[id].isMounted)
|
||||
.forEach(purgeDeep);
|
||||
|
||||
@@ -23,6 +23,11 @@ var ReactNativeOperationHistoryDevtool = {
|
||||
},
|
||||
|
||||
clearHistory() {
|
||||
if (ReactNativeOperationHistoryDevtool._preventClearing) {
|
||||
// Should only be used for tests.
|
||||
return;
|
||||
}
|
||||
|
||||
history = [];
|
||||
},
|
||||
|
||||
|
||||
@@ -97,13 +97,20 @@ describe('ReactComponentTreeDevtool', () => {
|
||||
// Ensure the tree is correct on every step.
|
||||
pairs.forEach(([element, expectedTree]) => {
|
||||
currentElement = element;
|
||||
|
||||
// Mount a new tree or update the existing tree.
|
||||
ReactDOM.render(<Wrapper />, node);
|
||||
expect(getActualTree()).toEqual(expectedTree);
|
||||
|
||||
// Purging should have no effect
|
||||
// on the tree we expect to see.
|
||||
ReactComponentTreeDevtool.purgeUnmountedComponents();
|
||||
expect(getActualTree()).toEqual(expectedTree);
|
||||
});
|
||||
|
||||
// Unmounting the root node should purge
|
||||
// the whole subtree automatically.
|
||||
ReactDOM.unmountComponentAtNode(node);
|
||||
ReactComponentTreeDevtool.purgeUnmountedComponents();
|
||||
expect(getActualTree()).toBe(undefined);
|
||||
expect(getRootDisplayNames()).toEqual([]);
|
||||
expect(getRegisteredDisplayNames()).toEqual([]);
|
||||
@@ -112,8 +119,23 @@ describe('ReactComponentTreeDevtool', () => {
|
||||
// Ensure the tree is correct on every step.
|
||||
pairs.forEach(([element, expectedTree]) => {
|
||||
currentElement = element;
|
||||
|
||||
// Rendering to string should not produce any entries
|
||||
// because ReactDebugTool purges it when the flush ends.
|
||||
ReactDOMServer.renderToString(<Wrapper />);
|
||||
expect(getActualTree()).toBe(undefined);
|
||||
expect(getRootDisplayNames()).toEqual([]);
|
||||
expect(getRegisteredDisplayNames()).toEqual([]);
|
||||
|
||||
// To test it, we tell the devtool to ignore next purge
|
||||
// so the cleanup request by ReactDebugTool is ignored.
|
||||
// This lets us make assertions on the actual tree.
|
||||
ReactComponentTreeDevtool._preventPurging = true;
|
||||
ReactDOMServer.renderToString(<Wrapper />);
|
||||
ReactComponentTreeDevtool._preventPurging = false;
|
||||
expect(getActualTree()).toEqual(expectedTree);
|
||||
|
||||
// Purge manually since we skipped the automatic purge.
|
||||
ReactComponentTreeDevtool.purgeUnmountedComponents();
|
||||
expect(getActualTree()).toBe(undefined);
|
||||
expect(getRootDisplayNames()).toEqual([]);
|
||||
@@ -1631,7 +1653,7 @@ describe('ReactComponentTreeDevtool', () => {
|
||||
assertTreeMatches([element, tree], {includeOwnerDisplayName: true});
|
||||
});
|
||||
|
||||
it('preserves unmounted components until purge', () => {
|
||||
it('purges unmounted components automatically', () => {
|
||||
var node = document.createElement('div');
|
||||
var renderBar = true;
|
||||
var fooInstance;
|
||||
@@ -1666,31 +1688,15 @@ describe('ReactComponentTreeDevtool', () => {
|
||||
renderBar = false;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
expect(
|
||||
getTree(barInstance._debugID, {
|
||||
includeParentDisplayName: true,
|
||||
expectedParentID: fooInstance._debugID,
|
||||
})
|
||||
getTree(barInstance._debugID, {expectedParentID: null})
|
||||
).toEqual({
|
||||
displayName: 'Bar',
|
||||
parentDisplayName: 'Foo',
|
||||
displayName: 'Unknown',
|
||||
children: [],
|
||||
});
|
||||
|
||||
ReactDOM.unmountComponentAtNode(node);
|
||||
expect(
|
||||
getTree(barInstance._debugID, {
|
||||
includeParentDisplayName: true,
|
||||
expectedParentID: fooInstance._debugID,
|
||||
})
|
||||
).toEqual({
|
||||
displayName: 'Bar',
|
||||
parentDisplayName: 'Foo',
|
||||
children: [],
|
||||
});
|
||||
|
||||
ReactComponentTreeDevtool.purgeUnmountedComponents();
|
||||
expect(
|
||||
getTree(barInstance._debugID, {includeParentDisplayName: true})
|
||||
getTree(barInstance._debugID, {expectedParentID: null})
|
||||
).toEqual({
|
||||
displayName: 'Unknown',
|
||||
children: [],
|
||||
@@ -1719,7 +1725,7 @@ describe('ReactComponentTreeDevtool', () => {
|
||||
|
||||
ReactDOM.unmountComponentAtNode(node);
|
||||
expect(ReactComponentTreeDevtool.getUpdateCount(divID)).toEqual(0);
|
||||
expect(ReactComponentTreeDevtool.getUpdateCount(spanID)).toEqual(2);
|
||||
expect(ReactComponentTreeDevtool.getUpdateCount(spanID)).toEqual(0);
|
||||
});
|
||||
|
||||
it('does not report top-level wrapper as a root', () => {
|
||||
@@ -1733,12 +1739,6 @@ describe('ReactComponentTreeDevtool', () => {
|
||||
|
||||
ReactDOM.unmountComponentAtNode(node);
|
||||
expect(getRootDisplayNames()).toEqual([]);
|
||||
|
||||
ReactComponentTreeDevtool.purgeUnmountedComponents();
|
||||
expect(getRootDisplayNames()).toEqual([]);
|
||||
|
||||
// This currently contains TopLevelWrapper until purge
|
||||
// so we only check it at the very end.
|
||||
expect(getRegisteredDisplayNames()).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -119,13 +119,20 @@ describe('ReactComponentTreeDevtool', () => {
|
||||
// Ensure the tree is correct on every step.
|
||||
pairs.forEach(([element, expectedTree]) => {
|
||||
currentElement = element;
|
||||
|
||||
// Mount a new tree or update the existing tree.
|
||||
ReactNative.render(<Wrapper />, 1);
|
||||
expect(getActualTree()).toEqual(expectedTree);
|
||||
|
||||
// Purging should have no effect
|
||||
// on the tree we expect to see.
|
||||
ReactComponentTreeDevtool.purgeUnmountedComponents();
|
||||
expect(getActualTree()).toEqual(expectedTree);
|
||||
});
|
||||
|
||||
// Unmounting the root node should purge
|
||||
// the whole subtree automatically.
|
||||
ReactNative.unmountComponentAtNode(1);
|
||||
ReactComponentTreeDevtool.purgeUnmountedComponents();
|
||||
expect(getActualTree()).toBe(undefined);
|
||||
expect(getRootDisplayNames()).toEqual([]);
|
||||
expect(getRegisteredDisplayNames()).toEqual([]);
|
||||
@@ -134,10 +141,13 @@ describe('ReactComponentTreeDevtool', () => {
|
||||
// Ensure the tree is correct on every step.
|
||||
pairs.forEach(([element, expectedTree]) => {
|
||||
currentElement = element;
|
||||
|
||||
// Mount a new tree.
|
||||
ReactNative.render(<Wrapper />, 1);
|
||||
ReactNative.unmountComponentAtNode(1);
|
||||
expect(getActualTree()).toEqual(expectedTree);
|
||||
ReactComponentTreeDevtool.purgeUnmountedComponents();
|
||||
|
||||
// Unmounting should clean it up.
|
||||
ReactNative.unmountComponentAtNode(1);
|
||||
expect(getActualTree()).toBe(undefined);
|
||||
expect(getRootDisplayNames()).toEqual([]);
|
||||
expect(getRegisteredDisplayNames()).toEqual([]);
|
||||
@@ -1620,7 +1630,7 @@ describe('ReactComponentTreeDevtool', () => {
|
||||
assertTreeMatches([element, tree], {includeOwnerDisplayName: true});
|
||||
});
|
||||
|
||||
it('preserves unmounted components until purge', () => {
|
||||
it('purges unmounted components automatically', () => {
|
||||
var renderBar = true;
|
||||
var fooInstance;
|
||||
var barInstance;
|
||||
@@ -1654,31 +1664,15 @@ describe('ReactComponentTreeDevtool', () => {
|
||||
renderBar = false;
|
||||
ReactNative.render(<Foo />, 1);
|
||||
expect(
|
||||
getTree(barInstance._debugID, {
|
||||
includeParentDisplayName: true,
|
||||
expectedParentID: fooInstance._debugID,
|
||||
})
|
||||
getTree(barInstance._debugID, {expectedParentID: null})
|
||||
).toEqual({
|
||||
displayName: 'Bar',
|
||||
parentDisplayName: 'Foo',
|
||||
displayName: 'Unknown',
|
||||
children: [],
|
||||
});
|
||||
|
||||
ReactNative.unmountComponentAtNode(1);
|
||||
expect(
|
||||
getTree(barInstance._debugID, {
|
||||
includeParentDisplayName: true,
|
||||
expectedParentID: fooInstance._debugID,
|
||||
})
|
||||
).toEqual({
|
||||
displayName: 'Bar',
|
||||
parentDisplayName: 'Foo',
|
||||
children: [],
|
||||
});
|
||||
|
||||
ReactComponentTreeDevtool.purgeUnmountedComponents();
|
||||
expect(
|
||||
getTree(barInstance._debugID, {includeParentDisplayName: true})
|
||||
getTree(barInstance._debugID, {expectedParentID: null})
|
||||
).toEqual({
|
||||
displayName: 'Unknown',
|
||||
children: [],
|
||||
@@ -1705,7 +1699,7 @@ describe('ReactComponentTreeDevtool', () => {
|
||||
|
||||
ReactNative.unmountComponentAtNode(1);
|
||||
expect(ReactComponentTreeDevtool.getUpdateCount(viewID)).toEqual(0);
|
||||
expect(ReactComponentTreeDevtool.getUpdateCount(imageID)).toEqual(2);
|
||||
expect(ReactComponentTreeDevtool.getUpdateCount(imageID)).toEqual(0);
|
||||
});
|
||||
|
||||
it('does not report top-level wrapper as a root', () => {
|
||||
@@ -1717,12 +1711,6 @@ describe('ReactComponentTreeDevtool', () => {
|
||||
|
||||
ReactNative.unmountComponentAtNode(1);
|
||||
expect(getRootDisplayNames()).toEqual([]);
|
||||
|
||||
ReactComponentTreeDevtool.purgeUnmountedComponents();
|
||||
expect(getRootDisplayNames()).toEqual([]);
|
||||
|
||||
// This currently contains TopLevelWrapper until purge
|
||||
// so we only check it at the very end.
|
||||
expect(getRegisteredDisplayNames()).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,9 +36,10 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
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);
|
||||
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'mount',
|
||||
@@ -53,7 +54,10 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
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,
|
||||
@@ -70,6 +74,8 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
return null;
|
||||
}
|
||||
var node = document.createElement('div');
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
// Empty DOM components should be invisible to devtools.
|
||||
@@ -82,11 +88,12 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
return element;
|
||||
}
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
|
||||
var node = document.createElement('div');
|
||||
element = null;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
element = <span />;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
@@ -104,12 +111,14 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
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);
|
||||
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
if (ReactDOMFeatureFlags.useCreateElement) {
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
@@ -138,7 +147,7 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
ReactDOM.render(<div />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div style={{ color: 'red' }} />, node);
|
||||
ReactDOM.render(<div style={{
|
||||
color: 'blue',
|
||||
@@ -171,7 +180,7 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
ReactDOM.render(<div />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div style={{
|
||||
color: 'red',
|
||||
backgroundColor: 'yellow',
|
||||
@@ -196,9 +205,11 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
describe('simple attribute', () => {
|
||||
it('gets recorded during mount', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div className="rad" tabIndex={42} />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div className="rad" tabIndex={42} />, node);
|
||||
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
if (ReactDOMFeatureFlags.useCreateElement) {
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
@@ -228,10 +239,11 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
ReactDOM.render(<div />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
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',
|
||||
@@ -262,9 +274,10 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
ReactDOM.render(<div />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div disabled={true} />, node);
|
||||
ReactDOM.render(<div disabled={false} />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'update attribute',
|
||||
@@ -280,9 +293,11 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
describe('custom attribute', () => {
|
||||
it('gets recorded during mount', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div data-x="rad" data-y={42} />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
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,
|
||||
@@ -312,10 +327,11 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
ReactDOM.render(<div />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
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',
|
||||
@@ -343,9 +359,11 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
describe('attribute on a web component', () => {
|
||||
it('gets recorded during mount', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<my-component className="rad" tabIndex={42} />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
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,
|
||||
@@ -375,10 +393,11 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
ReactDOM.render(<my-component />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
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',
|
||||
@@ -411,8 +430,9 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
ReactDOM.render(<div>Hi.</div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div>Bye.</div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace text',
|
||||
@@ -425,8 +445,9 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
ReactDOM.render(<div dangerouslySetInnerHTML={{__html: 'Hi.'}} />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div>Bye.</div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace text',
|
||||
@@ -439,8 +460,9 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
ReactDOM.render(<div><span /><p /></div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div>Bye.</div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove child',
|
||||
@@ -460,8 +482,9 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div>Hi.</div>, node);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div>Hi.</div>, node);
|
||||
|
||||
assertHistoryMatches([]);
|
||||
});
|
||||
});
|
||||
@@ -473,8 +496,9 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
var inst1 = ReactDOMComponentTree.getInstanceFromNode(node.firstChild.childNodes[0]);
|
||||
var inst2 = ReactDOMComponentTree.getInstanceFromNode(node.firstChild.childNodes[3]);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div>{'Bye.'}{43}</div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst1._debugID,
|
||||
type: 'replace text',
|
||||
@@ -490,8 +514,9 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
var node = document.createElement('div');
|
||||
ReactDOM.render(<div>{'Hi.'}{42}</div>, node);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div>{'Hi.'}{42}</div>, node);
|
||||
|
||||
assertHistoryMatches([]);
|
||||
});
|
||||
});
|
||||
@@ -509,9 +534,11 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
ReactDOM.render(<Foo />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
element = <span />;
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace with',
|
||||
@@ -528,11 +555,13 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
var node = document.createElement('div');
|
||||
element = <span />;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
element = null;
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace with',
|
||||
@@ -550,9 +579,11 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
element = <div />;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
element = <div />;
|
||||
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<Foo />, node);
|
||||
|
||||
assertHistoryMatches([]);
|
||||
});
|
||||
});
|
||||
@@ -563,11 +594,12 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
ReactDOM.render(<div>Hi.</div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(
|
||||
<div dangerouslySetInnerHTML={{__html: 'Bye.'}} />,
|
||||
node
|
||||
);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace children',
|
||||
@@ -583,11 +615,12 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(
|
||||
<div dangerouslySetInnerHTML={{__html: 'Bye.'}} />,
|
||||
node
|
||||
);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'replace children',
|
||||
@@ -600,11 +633,12 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
ReactDOM.render(<div><span /><p /></div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(
|
||||
<div dangerouslySetInnerHTML={{__html: 'Hi.'}} />,
|
||||
node
|
||||
);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove child',
|
||||
@@ -627,11 +661,12 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
node
|
||||
);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(
|
||||
<div dangerouslySetInnerHTML={{__html: 'Hi.'}} />,
|
||||
node
|
||||
);
|
||||
|
||||
assertHistoryMatches([]);
|
||||
});
|
||||
});
|
||||
@@ -642,8 +677,9 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
ReactDOM.render(<div><span /></div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div><span /><p /></div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'insert child',
|
||||
@@ -658,8 +694,9 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
ReactDOM.render(<div><span key="a" /><p key="b" /></div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div><p key="b" /><span key="a" /></div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'move child',
|
||||
@@ -674,8 +711,9 @@ describe('ReactNativeOperationHistoryDevtool', () => {
|
||||
ReactDOM.render(<div><span key="a" /><p key="b" /></div>, node);
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
|
||||
|
||||
ReactNativeOperationHistoryDevtool.clearHistory();
|
||||
ReactNativeOperationHistoryDevtool._preventClearing = true;
|
||||
ReactDOM.render(<div><span key="a" /></div>, node);
|
||||
|
||||
assertHistoryMatches([{
|
||||
instanceID: inst._debugID,
|
||||
type: 'remove child',
|
||||
|
||||
@@ -37,6 +37,9 @@ function renderToStringImpl(element, makeStaticMarkup) {
|
||||
transaction = ReactServerRenderingTransaction.getPooled(makeStaticMarkup);
|
||||
|
||||
return transaction.perform(function() {
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onBeginFlush();
|
||||
}
|
||||
var componentInstance = instantiateReactComponent(element);
|
||||
var markup = ReactReconciler.mountComponent(
|
||||
componentInstance,
|
||||
@@ -49,6 +52,7 @@ function renderToStringImpl(element, makeStaticMarkup) {
|
||||
ReactInstrumentation.debugTool.onUnmountComponent(
|
||||
componentInstance._debugID
|
||||
);
|
||||
ReactInstrumentation.debugTool.onEndFlush();
|
||||
}
|
||||
if (!makeStaticMarkup) {
|
||||
markup = ReactMarkupChecksum.addChecksumToMarkup(markup);
|
||||
|
||||
@@ -216,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;
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user