Skip to content

Commit b2c1380

Browse files
author
Brian Vaughn
committed
Added early bailouts to DevTools when generating preview strings for iterables/objects/arrays, to avoid doing unnecessary work
1 parent 3a75140 commit b2c1380

File tree

1 file changed

+59
-29
lines changed
  • packages/react-devtools-shared/src

1 file changed

+59
-29
lines changed

packages/react-devtools-shared/src/utils.js

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -448,14 +448,11 @@ export function getDisplayNameForReactElement(
448448
}
449449
}
450450

451-
const STRINGIFY_OPTIONS = {
452-
inlineCharacterLimit: 50,
453-
singleQuotes: false,
454-
};
451+
const MAX_PREVIEW_STRING_LENGTH = 50;
455452

456453
function truncateForDisplay(
457454
string: string,
458-
length: number = STRINGIFY_OPTIONS.inlineCharacterLimit,
455+
length: number = MAX_PREVIEW_STRING_LENGTH,
459456
) {
460457
if (string.length > length) {
461458
return string.substr(0, length) + '…';
@@ -519,11 +516,18 @@ export function formatDataForPreview(
519516
return `DataView(${data.buffer.byteLength})`;
520517
case 'array':
521518
if (showFormattedValue) {
522-
const formatted = [];
519+
let formatted = '';
523520
for (let i = 0; i < data.length; i++) {
524-
formatted[i] = formatDataForPreview(data[i], false);
521+
if (i > 0) {
522+
formatted += ', ';
523+
}
524+
formatted += formatDataForPreview(data[i], false);
525+
if (formatted.length > MAX_PREVIEW_STRING_LENGTH) {
526+
// Prevent doing a lot of unnecessary iteration...
527+
break;
528+
}
525529
}
526-
return `[${truncateForDisplay(formatted.join(', '))}]`;
530+
return `[${truncateForDisplay(formatted)}]`;
527531
} else {
528532
const length = data.hasOwnProperty(meta.size)
529533
? data[meta.size]
@@ -533,24 +537,38 @@ export function formatDataForPreview(
533537
case 'typed_array':
534538
const shortName = `${data.constructor.name}(${data.length})`;
535539
if (showFormattedValue) {
536-
return `${shortName} [${truncateForDisplay(
537-
data
538-
.toString()
539-
.split(',')
540-
.join(', '),
541-
)}]`;
540+
let formatted = '';
541+
for (let i = 0; i < data.length; i++) {
542+
if (i > 0) {
543+
formatted += ', ';
544+
}
545+
formatted += data[i];
546+
if (formatted.length > MAX_PREVIEW_STRING_LENGTH) {
547+
// Prevent doing a lot of unnecessary iteration...
548+
break;
549+
}
550+
}
551+
return `${shortName} [${truncateForDisplay(formatted)}]`;
542552
} else {
543553
return shortName;
544554
}
545555
case 'iterator':
546556
const name = data.constructor.name;
547557
if (showFormattedValue) {
548-
const valueStrings = [];
549558
// TRICKY
550559
// Don't use [...spread] syntax for this purpose.
551560
// This project uses @babel/plugin-transform-spread in "loose" mode which only works with Array values.
552561
// Other types (e.g. typed arrays, Sets) will not spread correctly.
553-
Array.from(data).forEach(entryOrEntries => {
562+
const array = Array.from(data);
563+
564+
let formatted = '';
565+
for (let i = 0; i < array.length; i++) {
566+
const entryOrEntries = array[i];
567+
568+
if (i > 0) {
569+
formatted += ', ';
570+
}
571+
554572
// TRICKY
555573
// Browsers display Maps and Sets differently.
556574
// To mimic their behavior, detect if we've been given an entries tuple.
@@ -559,28 +577,40 @@ export function formatDataForPreview(
559577
if (Array.isArray(entryOrEntries)) {
560578
const key = formatDataForPreview(entryOrEntries[0], true);
561579
const value = formatDataForPreview(entryOrEntries[1], false);
562-
valueStrings.push(`${key} => ${value}`);
580+
formatted += `${key} => ${value}`;
563581
} else {
564-
valueStrings.push(formatDataForPreview(entryOrEntries, false));
582+
formatted += formatDataForPreview(entryOrEntries, false);
583+
}
584+
585+
if (formatted.length > MAX_PREVIEW_STRING_LENGTH) {
586+
// Prevent doing a lot of unnecessary iteration...
587+
break;
565588
}
566-
});
567-
return `${name}(${data.size}) {${truncateForDisplay(
568-
valueStrings.join(', '),
569-
)}}`;
589+
}
590+
591+
return `${name}(${data.size}) {${truncateForDisplay(formatted)}}`;
570592
} else {
571593
return `${name}(${data.size})`;
572594
}
573595
case 'date':
574596
return data.toString();
575597
case 'object':
576598
if (showFormattedValue) {
577-
const formatted = [];
578-
Object.keys(data)
579-
.sort(alphaSortEntries)
580-
.forEach(key => {
581-
formatted.push(`${key}: ${formatDataForPreview(data[key], false)}`);
582-
});
583-
return `{${truncateForDisplay(formatted.join(', '))}}`;
599+
const keys = Object.keys(data).sort(alphaSortEntries);
600+
601+
let formatted = '';
602+
for (let i = 0; i < keys.length; i++) {
603+
const key = keys[i];
604+
if (i > 0) {
605+
formatted += ', ';
606+
}
607+
formatted += `${key}: ${formatDataForPreview(data[key], false)}`;
608+
if (formatted.length > MAX_PREVIEW_STRING_LENGTH) {
609+
// Prevent doing a lot of unnecessary iteration...
610+
break;
611+
}
612+
}
613+
return `{${truncateForDisplay(formatted)}}`;
584614
} else {
585615
return '{…}';
586616
}

0 commit comments

Comments
 (0)