(subscription);
- const reloadAndProfile = useCallback(() => bridge.send('reloadAndProfile'), [
- bridge,
- ]);
+ const reloadAndProfile = useCallback(
+ () => bridge.send('reloadAndProfile', recordChangeDescriptions),
+ [bridge, recordChangeDescriptions]
+ );
if (!supportsReloadAndProfile) {
return null;
diff --git a/src/devtools/views/Profiler/SidebarCommitInfo.css b/src/devtools/views/Profiler/SidebarCommitInfo.css
index 0bbe6674..6a835e64 100644
--- a/src/devtools/views/Profiler/SidebarCommitInfo.css
+++ b/src/devtools/views/Profiler/SidebarCommitInfo.css
@@ -4,12 +4,12 @@
flex: 0 0 auto;
display: flex;
align-items: center;
+ border-bottom: 1px solid var(--color-border);
}
.Content {
padding: 0.5rem;
user-select: none;
- border-top: 1px solid var(--color-border);
overflow: auto;
}
diff --git a/src/devtools/views/Profiler/SidebarSelectedFiberInfo.css b/src/devtools/views/Profiler/SidebarSelectedFiberInfo.css
index 30097488..a89175b6 100644
--- a/src/devtools/views/Profiler/SidebarSelectedFiberInfo.css
+++ b/src/devtools/views/Profiler/SidebarSelectedFiberInfo.css
@@ -4,18 +4,21 @@
flex: 0 0 auto;
display: flex;
align-items: center;
+ border-bottom: 1px solid var(--color-border);
}
.Content {
padding: 0.5rem;
user-select: none;
- border-top: 1px solid var(--color-border);
overflow-y: auto;
}
.Component {
flex: 1;
color: var(--color-component-name);
+ white-space: nowrap;
+ overflow-x: hidden;
+ text-overflow: ellipsis;
}
.Component:before {
white-space: nowrap;
@@ -56,3 +59,22 @@
.CurrentCommit:focus {
outline: none;
}
+
+.WhatChangedItem {
+ margin-top: 0.25rem;
+}
+
+.WhatChangedKey {
+ font-family: var(--font-family-monospace);
+ font-size: var(--font-size-monospace-small);
+ line-height: 1;
+}
+.WhatChangedKey:first-of-type::before {
+ content: ' (';
+}
+.WhatChangedKey::after {
+ content: ', ';
+}
+.WhatChangedKey:last-of-type::after {
+ content: ')';
+}
diff --git a/src/devtools/views/Profiler/SidebarSelectedFiberInfo.js b/src/devtools/views/Profiler/SidebarSelectedFiberInfo.js
index c83b0bc0..839b95d2 100644
--- a/src/devtools/views/Profiler/SidebarSelectedFiberInfo.js
+++ b/src/devtools/views/Profiler/SidebarSelectedFiberInfo.js
@@ -1,6 +1,7 @@
// @flow
import React, { Fragment, useContext } from 'react';
+import ProfilerStore from 'src/devtools/ProfilerStore';
import { ProfilerContext } from './ProfilerContext';
import { formatDuration, formatTime } from './utils';
import { StoreContext } from '../context';
@@ -29,7 +30,7 @@ export default function SidebarSelectedFiberInfo(_: Props) {
});
const listItems = [];
- for (let i = 0; i < commitIndices.length; i += 2) {
+ for (let i = 0; i < commitIndices.length; i++) {
const commitIndex = commitIndices[i];
const { duration, timestamp } = profilerStore.getCommitData(
@@ -67,9 +68,140 @@ export default function SidebarSelectedFiberInfo(_: Props) {
+
-
: {listItems}
+ {listItems.length > 0 && (
+
+ : {listItems}
+
+ )}
+ {listItems.length === 0 && (
+
Did not render during this profiling session.
+ )}
);
}
+
+type WhatChangedProps = {|
+ commitIndex: number,
+ fiberID: number,
+ profilerStore: ProfilerStore,
+ rootID: number,
+|};
+
+function WhatChanged({
+ commitIndex,
+ fiberID,
+ profilerStore,
+ rootID,
+}: WhatChangedProps) {
+ const { changeDescriptions } = profilerStore.getCommitData(
+ ((rootID: any): number),
+ commitIndex
+ );
+ if (changeDescriptions === null) {
+ return null;
+ }
+
+ const changeDescription = changeDescriptions.get(fiberID);
+ if (changeDescription == null) {
+ return null;
+ }
+
+ if (changeDescription.isFirstMount) {
+ return (
+
+
+
+ This is the first time the component rendered.
+
+
+ );
+ }
+
+ const changes = [];
+
+ if (changeDescription.context === true) {
+ changes.push(
+
+ • Context changed
+
+ );
+ } else if (
+ typeof changeDescription.context === 'object' &&
+ changeDescription.context !== null &&
+ changeDescription.context.length !== 0
+ ) {
+ changes.push(
+
+ • Context changed:
+ {changeDescription.context.map(key => (
+
+ {key}
+
+ ))}
+
+ );
+ }
+
+ if (changeDescription.didHooksChange) {
+ changes.push(
+
+ • Hooks changed
+
+ );
+ }
+
+ if (
+ changeDescription.props !== null &&
+ changeDescription.props.length !== 0
+ ) {
+ changes.push(
+
+ • Props changed:
+ {changeDescription.props.map(key => (
+
+ {key}
+
+ ))}
+
+ );
+ }
+
+ if (
+ changeDescription.state !== null &&
+ changeDescription.state.length !== 0
+ ) {
+ changes.push(
+
+ • State changed:
+ {changeDescription.state.map(key => (
+
+ {key}
+
+ ))}
+
+ );
+ }
+
+ if (changes.length === 0) {
+ changes.push(
+
+ The parent component rendered.
+
+ );
+ }
+
+ return (
+
+
+ {changes}
+
+ );
+}
diff --git a/src/devtools/views/Profiler/types.js b/src/devtools/views/Profiler/types.js
index 9af30779..c99a79fe 100644
--- a/src/devtools/views/Profiler/types.js
+++ b/src/devtools/views/Profiler/types.js
@@ -31,7 +31,18 @@ export type SnapshotNode = {|
type: ElementType,
|};
+export type ChangeDescription = {|
+ context: Array | boolean | null,
+ didHooksChange: boolean,
+ isFirstMount: boolean,
+ props: Array | null,
+ state: Array | null,
+|};
+
export type CommitDataFrontend = {|
+ // Map of Fiber (ID) to a description of what changed in this commit.
+ changeDescriptions: Map | null,
+
// How long was this commit?
duration: number,
@@ -93,6 +104,7 @@ export type ProfilingDataFrontend = {|
|};
export type CommitDataExport = {|
+ changeDescriptions: Array<[number, ChangeDescription]> | null,
duration: number,
// Tuple of fiber ID and actual duration
fiberActualDurations: Array<[number, number]>,
diff --git a/src/devtools/views/Profiler/utils.js b/src/devtools/views/Profiler/utils.js
index f192520f..bd4d1e9f 100644
--- a/src/devtools/views/Profiler/utils.js
+++ b/src/devtools/views/Profiler/utils.js
@@ -58,6 +58,10 @@ export function prepareProfilingDataFrontendFromBackendAndStore(
dataForRoots.set(rootID, {
commitData: commitData.map((commitDataBackend, commitIndex) => ({
+ changeDescriptions:
+ commitDataBackend.changeDescriptions != null
+ ? new Map(commitDataBackend.changeDescriptions)
+ : null,
duration: commitDataBackend.duration,
fiberActualDurations: new Map(
commitDataBackend.fiberActualDurations
@@ -109,6 +113,7 @@ export function prepareProfilingDataFrontendFromExport(
dataForRoots.set(rootID, {
commitData: commitData.map(
({
+ changeDescriptions,
duration,
fiberActualDurations,
fiberSelfDurations,
@@ -117,6 +122,8 @@ export function prepareProfilingDataFrontendFromExport(
screenshot,
timestamp,
}) => ({
+ changeDescriptions:
+ changeDescriptions != null ? new Map(changeDescriptions) : null,
duration,
fiberActualDurations: new Map(fiberActualDurations),
fiberSelfDurations: new Map(fiberSelfDurations),
@@ -159,6 +166,7 @@ export function prepareProfilingDataExport(
dataForRoots.push({
commitData: commitData.map(
({
+ changeDescriptions,
duration,
fiberActualDurations,
fiberSelfDurations,
@@ -167,6 +175,10 @@ export function prepareProfilingDataExport(
screenshot,
timestamp,
}) => ({
+ changeDescriptions:
+ changeDescriptions != null
+ ? Array.from(changeDescriptions.entries())
+ : null,
duration,
fiberActualDurations: Array.from(fiberActualDurations.entries()),
fiberSelfDurations: Array.from(fiberSelfDurations.entries()),
diff --git a/src/devtools/views/Settings/Settings.js b/src/devtools/views/Settings/Settings.js
index 645ee254..3e29c19d 100644
--- a/src/devtools/views/Settings/Settings.js
+++ b/src/devtools/views/Settings/Settings.js
@@ -1,6 +1,6 @@
// @flow
-import React, { useCallback, useContext, useMemo } from 'react';
+import React, { Fragment, useCallback, useContext, useMemo } from 'react';
import { useSubscription } from '../hooks';
import { StoreContext } from '../context';
import { SettingsContext } from './SettingsContext';
@@ -43,6 +43,20 @@ function Settings(_: {||}) {
collapseNodesByDefaultSubscription
);
+ const recordChangeDescriptionsSubscription = useMemo(
+ () => ({
+ getCurrentValue: () => store.recordChangeDescriptions,
+ subscribe: (callback: Function) => {
+ store.addListener('recordChangeDescriptions', callback);
+ return () => store.removeListener('recordChangeDescriptions', callback);
+ },
+ }),
+ [store]
+ );
+ const recordChangeDescriptions = useSubscription(
+ recordChangeDescriptionsSubscription
+ );
+
const updateDisplayDensity = useCallback(
({ currentTarget }) => {
setDisplayDensity(currentTarget.value);
@@ -69,6 +83,12 @@ function Settings(_: {||}) {
},
[store]
);
+ const updateRecordChangeDescriptions = useCallback(
+ ({ currentTarget }) => {
+ store.recordChangeDescriptions = currentTarget.checked;
+ },
+ [store]
+ );
return (
@@ -145,25 +165,37 @@ function Settings(_: {||}) {
- {store.supportsCaptureScreenshots && (
-
-
Profiler
-
- {captureScreenshots && (
-
- Screenshots will be throttled in order to reduce the negative
- impact on performance.
-
- )}
-
- )}
+
+
Profiler
+
+
+
+ {store.supportsCaptureScreenshots && (
+
+
+ {captureScreenshots && (
+
+ Screenshots will be throttled in order to reduce the negative
+ impact on performance.
+
+ )}
+
+ )}
+
);
}