mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 12:20:20 +01:00
[DevTools] Hide State and Props in the Sidebar for Suspense (#34630)
We're showing too much noise in the side-panel when selecting a Suspense boundary. The interesting thing to see directly is the "Suspended by". The "props" are mostly useless because the `"name"` prop is already in the tree. I'm now also showing it in the title bar of the selected element panel. The "children" and "fallback" props are just the thing that you can see in the tree view anyway. The "state" is this weird section with just one field in it, which we already have duplicated in the top toolbar as well. We can just delete this. I make sure to show the icon and a "suspended..." section while the boundary is still loading but now yet resuspended by force suspending. While still loading: <img width="600" height="193" alt="Screenshot 2025-09-27 at 11 54 37 PM" src="https://github.com/user-attachments/assets/1c3f3a96-46e0-4b11-806f-032569c7d5b5" /> After loading: <img width="602" height="266" alt="Screenshot 2025-09-27 at 11 54 53 PM" src="https://github.com/user-attachments/assets/c43cc4cb-036f-4ced-9b0d-226c6320cd76" /> Resuspended after loading: <img width="602" height="300" alt="Screenshot 2025-09-27 at 11 55 07 PM" src="https://github.com/user-attachments/assets/0be01735-48a7-47dc-b5cf-e72ec71e0148" />
This commit is contained in:
parent
2e68dc76a4
commit
c825f03067
|
|
@ -6596,9 +6596,6 @@ export function attach(
|
||||||
rootType = fiberRoot._debugRootType;
|
rootType = fiberRoot._debugRootType;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isTimedOutSuspense =
|
|
||||||
tag === SuspenseComponent && memoizedState !== null;
|
|
||||||
|
|
||||||
let isErrored = false;
|
let isErrored = false;
|
||||||
if (isErrorBoundary(fiber)) {
|
if (isErrorBoundary(fiber)) {
|
||||||
// if the current inspected element is an error boundary,
|
// if the current inspected element is an error boundary,
|
||||||
|
|
@ -6670,7 +6667,7 @@ export function attach(
|
||||||
if (
|
if (
|
||||||
fiberInstance.suspenseNode !== null &&
|
fiberInstance.suspenseNode !== null &&
|
||||||
fiberInstance.suspenseNode.hasUnknownSuspenders &&
|
fiberInstance.suspenseNode.hasUnknownSuspenders &&
|
||||||
!isTimedOutSuspense
|
!isSuspended
|
||||||
) {
|
) {
|
||||||
// Something unknown threw to suspended this boundary. Let's figure out why that might be.
|
// Something unknown threw to suspended this boundary. Let's figure out why that might be.
|
||||||
if (renderer.bundleType === 0) {
|
if (renderer.bundleType === 0) {
|
||||||
|
|
@ -6708,7 +6705,7 @@ export function attach(
|
||||||
supportsTogglingSuspense &&
|
supportsTogglingSuspense &&
|
||||||
hasSuspenseBoundary &&
|
hasSuspenseBoundary &&
|
||||||
// If it's showing the real content, we can always flip fallback.
|
// If it's showing the real content, we can always flip fallback.
|
||||||
(!isTimedOutSuspense ||
|
(!isSuspended ||
|
||||||
// If it's showing fallback because we previously forced it to,
|
// If it's showing fallback because we previously forced it to,
|
||||||
// allow toggling it back to remove the fallback override.
|
// allow toggling it back to remove the fallback override.
|
||||||
forceFallbackForFibers.has(fiber) ||
|
forceFallbackForFibers.has(fiber) ||
|
||||||
|
|
|
||||||
|
|
@ -269,18 +269,21 @@ export default function InspectedElementWrapper(_: Props): React.Node {
|
||||||
<ButtonIcon type="error" />
|
<ButtonIcon type="error" />
|
||||||
</Toggle>
|
</Toggle>
|
||||||
)}
|
)}
|
||||||
{canToggleSuspense && (
|
{canToggleSuspense || isSuspended ? (
|
||||||
<Toggle
|
<Toggle
|
||||||
isChecked={isSuspended}
|
isChecked={isSuspended}
|
||||||
|
isDisabled={!canToggleSuspense}
|
||||||
onChange={toggleSuspended}
|
onChange={toggleSuspended}
|
||||||
title={
|
title={
|
||||||
isSuspended
|
isSuspended
|
||||||
? 'Unsuspend the selected component'
|
? canToggleSuspense
|
||||||
|
? 'Unsuspend the selected component'
|
||||||
|
: 'This boundary is still suspended'
|
||||||
: 'Suspend the selected component'
|
: 'Suspend the selected component'
|
||||||
}>
|
}>
|
||||||
<ButtonIcon type="suspend" />
|
<ButtonIcon type="suspend" />
|
||||||
</Toggle>
|
</Toggle>
|
||||||
)}
|
) : null}
|
||||||
{store.supportsInspectMatchingDOMElement && (
|
{store.supportsInspectMatchingDOMElement && (
|
||||||
<Button
|
<Button
|
||||||
onClick={highlightElement}
|
onClick={highlightElement}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,10 @@ import NewKeyValue from './NewKeyValue';
|
||||||
import {alphaSortEntries, serializeDataForCopy} from '../utils';
|
import {alphaSortEntries, serializeDataForCopy} from '../utils';
|
||||||
import Store from '../../store';
|
import Store from '../../store';
|
||||||
import styles from './InspectedElementSharedStyles.css';
|
import styles from './InspectedElementSharedStyles.css';
|
||||||
import {ElementTypeClass} from 'react-devtools-shared/src/frontend/types';
|
import {
|
||||||
|
ElementTypeClass,
|
||||||
|
ElementTypeSuspense,
|
||||||
|
} from 'react-devtools-shared/src/frontend/types';
|
||||||
import {withPermissionsCheck} from 'react-devtools-shared/src/frontend/utils/withPermissionsCheck';
|
import {withPermissionsCheck} from 'react-devtools-shared/src/frontend/utils/withPermissionsCheck';
|
||||||
|
|
||||||
import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
|
import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
|
||||||
|
|
@ -47,6 +50,16 @@ export default function InspectedElementPropsTree({
|
||||||
type,
|
type,
|
||||||
} = inspectedElement;
|
} = inspectedElement;
|
||||||
|
|
||||||
|
if (type === ElementTypeSuspense) {
|
||||||
|
// Skip showing the props for Suspense. We want to give more real estate to the
|
||||||
|
// "Suspended by" for Suspense boundaries. We could maybe show it further below
|
||||||
|
// but in practice, the props of Suspense boundaries are not very useful to
|
||||||
|
// inspect because the name shows in the tree already. The children in the tree
|
||||||
|
// will be either the "fallback" or "children" prop which you can already inspect
|
||||||
|
// but resuspending the tree.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const canDeletePaths =
|
const canDeletePaths =
|
||||||
type === ElementTypeClass || canEditFunctionPropsDeletePaths;
|
type === ElementTypeClass || canEditFunctionPropsDeletePaths;
|
||||||
const canEditValues =
|
const canEditValues =
|
||||||
|
|
|
||||||
|
|
@ -322,6 +322,17 @@ export default function InspectedElementSuspendedBy({
|
||||||
(suspendedBy == null || suspendedBy.length === 0) &&
|
(suspendedBy == null || suspendedBy.length === 0) &&
|
||||||
inspectedElement.unknownSuspenders === UNKNOWN_SUSPENDERS_NONE
|
inspectedElement.unknownSuspenders === UNKNOWN_SUSPENDERS_NONE
|
||||||
) {
|
) {
|
||||||
|
if (inspectedElement.isSuspended) {
|
||||||
|
// If we're still suspended, show a place holder until the data loads.
|
||||||
|
// We don't know what we're suspended by until it has loaded.
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={styles.HeaderRow}>
|
||||||
|
<div className={styles.Header}>suspended...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*
|
|
||||||
* @flow
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as React from 'react';
|
|
||||||
import {OptionsContext} from '../context';
|
|
||||||
import EditableValue from './EditableValue';
|
|
||||||
import Store from '../../store';
|
|
||||||
import {ElementTypeSuspense} from 'react-devtools-shared/src/frontend/types';
|
|
||||||
import styles from './InspectedElementSharedStyles.css';
|
|
||||||
|
|
||||||
import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
|
|
||||||
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
bridge: FrontendBridge,
|
|
||||||
inspectedElement: InspectedElement,
|
|
||||||
store: Store,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function InspectedElementSuspenseToggle({
|
|
||||||
bridge,
|
|
||||||
inspectedElement,
|
|
||||||
store,
|
|
||||||
}: Props): React.Node {
|
|
||||||
const {readOnly} = React.useContext(OptionsContext);
|
|
||||||
|
|
||||||
const {id, isSuspended, type} = inspectedElement;
|
|
||||||
const canToggleSuspense = !readOnly && inspectedElement.canToggleSuspense;
|
|
||||||
|
|
||||||
if (type !== ElementTypeSuspense) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const toggleSuspense = (path: any, value: boolean) => {
|
|
||||||
const rendererID = store.getRendererIDForElement(id);
|
|
||||||
if (rendererID !== null) {
|
|
||||||
bridge.send('overrideSuspense', {
|
|
||||||
id,
|
|
||||||
rendererID,
|
|
||||||
forceFallback: value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className={styles.HeaderRow}>
|
|
||||||
<div className={styles.Header}>suspense</div>
|
|
||||||
</div>
|
|
||||||
<div className={styles.ToggleSuspenseRow}>
|
|
||||||
<span className={styles.Name}>Suspended</span>
|
|
||||||
{canToggleSuspense ? (
|
|
||||||
// key is required to keep <EditableValue> and header row toggle button in sync
|
|
||||||
<EditableValue
|
|
||||||
key={isSuspended}
|
|
||||||
overrideValue={toggleSuspense}
|
|
||||||
path={['suspense', 'Suspended']}
|
|
||||||
value={isSuspended}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<span className={styles.Value}>{isSuspended ? 'true' : 'false'}</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -17,7 +17,6 @@ import InspectedElementHooksTree from './InspectedElementHooksTree';
|
||||||
import InspectedElementPropsTree from './InspectedElementPropsTree';
|
import InspectedElementPropsTree from './InspectedElementPropsTree';
|
||||||
import InspectedElementStateTree from './InspectedElementStateTree';
|
import InspectedElementStateTree from './InspectedElementStateTree';
|
||||||
import InspectedElementStyleXPlugin from './InspectedElementStyleXPlugin';
|
import InspectedElementStyleXPlugin from './InspectedElementStyleXPlugin';
|
||||||
import InspectedElementSuspenseToggle from './InspectedElementSuspenseToggle';
|
|
||||||
import InspectedElementSuspendedBy from './InspectedElementSuspendedBy';
|
import InspectedElementSuspendedBy from './InspectedElementSuspendedBy';
|
||||||
import NativeStyleEditor from './NativeStyleEditor';
|
import NativeStyleEditor from './NativeStyleEditor';
|
||||||
import {enableStyleXFeatures} from 'react-devtools-feature-flags';
|
import {enableStyleXFeatures} from 'react-devtools-feature-flags';
|
||||||
|
|
@ -95,14 +94,6 @@ export default function InspectedElementView({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.InspectedElementSection}>
|
|
||||||
<InspectedElementSuspenseToggle
|
|
||||||
bridge={bridge}
|
|
||||||
inspectedElement={inspectedElement}
|
|
||||||
store={store}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.InspectedElementSection}>
|
<div className={styles.InspectedElementSection}>
|
||||||
<InspectedElementStateTree
|
<InspectedElementStateTree
|
||||||
bridge={bridge}
|
bridge={bridge}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user