Skip to content

Commit 01b1e7e

Browse files
author
Brian Vaughn
committed
Alpha-sort props/state/context keys
1 parent d2456c7 commit 01b1e7e

4 files changed

Lines changed: 43 additions & 7 deletions

File tree

src/devtools/views/Components/HooksTree.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ function HookView({
164164
) : (
165165
<KeyValue
166166
depth={1}
167+
alphaSort={false}
167168
inspectPath={inspectPath}
168169
name="subHooks"
169170
path={path.concat(['subHooks'])}
@@ -186,6 +187,7 @@ function HookView({
186187
<div className={styles.Children} hidden={!isOpen}>
187188
<KeyValue
188189
depth={1}
190+
alphaSort={false}
189191
inspectPath={inspectPath}
190192
name="DebugValue"
191193
path={path.concat(['value'])}
@@ -242,6 +244,7 @@ function HookView({
242244
<div className={styles.Hook}>
243245
<KeyValue
244246
depth={1}
247+
alphaSort={false}
245248
inspectPath={inspectPath}
246249
name={name}
247250
overrideValueFn={overrideValueFn}

src/devtools/views/Components/InspectedElementTree.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import React, { useCallback } from 'react';
55
import Button from '../Button';
66
import ButtonIcon from '../ButtonIcon';
77
import KeyValue from './KeyValue';
8-
import { serializeDataForCopy } from '../utils';
8+
import { alphaSortEntries, serializeDataForCopy } from '../utils';
99
import styles from './InspectedElementTree.css';
1010

1111
import type { InspectPath } from './SelectedElement';
@@ -27,7 +27,12 @@ export default function InspectedElementTree({
2727
overrideValueFn,
2828
showWhenEmpty = false,
2929
}: Props) {
30-
const isEmpty = data === null || Object.keys(data).length === 0;
30+
const entries = data != null ? Object.entries(data) : null;
31+
if (entries !== null) {
32+
entries.sort(alphaSortEntries);
33+
}
34+
35+
const isEmpty = entries === null || entries.length === 0;
3136

3237
const handleCopy = useCallback(() => copy(serializeDataForCopy(data)), [
3338
data,
@@ -48,15 +53,16 @@ export default function InspectedElementTree({
4853
</div>
4954
{isEmpty && <div className={styles.Empty}>None</div>}
5055
{!isEmpty &&
51-
Object.keys((data: any)).map(name => (
56+
(entries: any).map(([name, value]) => (
5257
<KeyValue
5358
key={name}
59+
alphaSort={true}
5460
depth={1}
5561
inspectPath={inspectPath}
5662
name={name}
5763
overrideValueFn={overrideValueFn}
5864
path={[name]}
59-
value={(data: any)[name]}
65+
value={value}
6066
/>
6167
))}
6268
</div>

src/devtools/views/Components/KeyValue.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import React, { useEffect, useRef, useState } from 'react';
44
import type { Element } from 'react';
55
import EditableValue from './EditableValue';
66
import ExpandCollapseToggle from './ExpandCollapseToggle';
7-
import { getMetaValueLabel } from '../utils';
7+
import { alphaSortEntries, getMetaValueLabel } from '../utils';
88
import { meta } from '../../../hydration';
99
import styles from './KeyValue.css';
1010

@@ -13,6 +13,7 @@ import type { InspectPath } from './SelectedElement';
1313
type OverrideValueFn = (path: Array<string | number>, value: any) => void;
1414

1515
type KeyValueProps = {|
16+
alphaSort: boolean,
1617
depth: number,
1718
hidden?: boolean,
1819
inspectPath?: InspectPath,
@@ -24,6 +25,7 @@ type KeyValueProps = {|
2425
|};
2526

2627
export default function KeyValue({
28+
alphaSort,
2729
depth,
2830
inspectPath,
2931
isReadOnly,
@@ -127,6 +129,7 @@ export default function KeyValue({
127129
children = value.map((innerValue, index) => (
128130
<KeyValue
129131
key={index}
132+
alphaSort={alphaSort}
130133
depth={depth + 1}
131134
inspectPath={inspectPath}
132135
isReadOnly={isReadOnly}
@@ -162,15 +165,24 @@ export default function KeyValue({
162165
</div>
163166
);
164167
} else {
165-
const hasChildren = Object.entries(value).length > 0;
168+
// TRICKY
169+
// It's important to use Object.entries() rather than Object.keys()
170+
// because of the hidden meta Symbols used for hydration and unserializable values.
171+
const entries = Object.entries(value);
172+
if (alphaSort) {
173+
entries.sort(alphaSortEntries);
174+
}
175+
176+
const hasChildren = entries.length > 0;
166177
const displayName = value.hasOwnProperty(meta.unserializable)
167178
? getMetaValueLabel(value)
168179
: 'Object';
169180

170181
let areChildrenReadOnly = isReadOnly || !!value[meta.readonly];
171-
children = Object.entries(value).map<Element<any>>(([name, value]) => (
182+
children = entries.map<Element<any>>(([name, value]) => (
172183
<KeyValue
173184
key={name}
185+
alphaSort={alphaSort}
174186
depth={depth + 1}
175187
inspectPath={inspectPath}
176188
isReadOnly={areChildrenReadOnly}

src/devtools/views/utils.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@ import { meta } from '../../hydration';
55

66
import type { HooksTree } from 'src/backend/types';
77

8+
export function alphaSortEntries(
9+
entryA: [string, mixed],
10+
entryB: [string, mixed]
11+
): number {
12+
const a = entryA[0];
13+
const b = entryB[0];
14+
if ('' + +a === a) {
15+
if ('' + +b !== b) {
16+
return -1;
17+
}
18+
return +a < +b ? -1 : 1;
19+
}
20+
return a < b ? -1 : 1;
21+
}
22+
823
export function createRegExp(string: string): RegExp {
924
// Allow /regex/ syntax with optional last /
1025
if (string[0] === '/') {

0 commit comments

Comments
 (0)