Skip to content

Commit e71a639

Browse files
authored
[Perf Tracks] Don't enumerate typed array props in dev (react#36913)
1 parent 3508aee commit e71a639

2 files changed

Lines changed: 83 additions & 105 deletions

File tree

packages/react-reconciler/src/__tests__/ReactPerformanceTrack-test.js

Lines changed: 63 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -232,111 +232,69 @@ describe('ReactPerformanceTracks', () => {
232232
['Changed Props', ''],
233233
['  data', ''],
234234
['-   buffer', 'null'],
235-
['+   buffer', 'Uint8Array'],
236-
['+     0', '0'],
237-
['+     1', '0'],
238-
['+     2', '0'],
239-
['+     3', '0'],
240-
['+     4', '0'],
241-
['+     5', '0'],
242-
['+     6', '0'],
243-
['+     7', '0'],
244-
['+     8', '0'],
245-
['+     9', '0'],
246-
['+     10', '0'],
247-
['+     11', '0'],
248-
['+     12', '0'],
249-
['+     13', '0'],
250-
['+     14', '0'],
251-
['+     15', '0'],
252-
['+     16', '0'],
253-
['+     17', '0'],
254-
['+     18', '0'],
255-
['+     19', '0'],
256-
['+     20', '0'],
257-
['+     21', '0'],
258-
['+     22', '0'],
259-
['+     23', '0'],
260-
['+     24', '0'],
261-
['+     25', '0'],
262-
['+     26', '0'],
263-
['+     27', '0'],
264-
['+     28', '0'],
265-
['+     29', '0'],
266-
['+     30', '0'],
267-
['+     31', '0'],
268-
['+     32', '0'],
269-
['+     33', '0'],
270-
['+     34', '0'],
271-
['+     35', '0'],
272-
['+     36', '0'],
273-
['+     37', '0'],
274-
['+     38', '0'],
275-
['+     39', '0'],
276-
['+     40', '0'],
277-
['+     41', '0'],
278-
['+     42', '0'],
279-
['+     43', '0'],
280-
['+     44', '0'],
281-
['+     45', '0'],
282-
['+     46', '0'],
283-
['+     47', '0'],
284-
['+     48', '0'],
285-
['+     49', '0'],
286-
['+     50', '0'],
287-
['+     51', '0'],
288-
['+     52', '0'],
289-
['+     53', '0'],
290-
['+     54', '0'],
291-
['+     55', '0'],
292-
['+     56', '0'],
293-
['+     57', '0'],
294-
['+     58', '0'],
295-
['+     59', '0'],
296-
['+     60', '0'],
297-
['+     61', '0'],
298-
['+     62', '0'],
299-
['+     63', '0'],
300-
['+     64', '0'],
301-
['+     65', '0'],
302-
['+     66', '0'],
303-
['+     67', '0'],
304-
['+     68', '0'],
305-
['+     69', '0'],
306-
['+     70', '0'],
307-
['+     71', '0'],
308-
['+     72', '0'],
309-
['+     73', '0'],
310-
['+     74', '0'],
311-
['+     75', '0'],
312-
['+     76', '0'],
313-
['+     77', '0'],
314-
['+     78', '0'],
315-
['+     79', '0'],
316-
['+     80', '0'],
317-
['+     81', '0'],
318-
['+     82', '0'],
319-
['+     83', '0'],
320-
['+     84', '0'],
321-
['+     85', '0'],
322-
['+     86', '0'],
323-
['+     87', '0'],
324-
['+     88', '0'],
325-
['+     89', '0'],
326-
['+     90', '0'],
327-
['+     91', '0'],
328-
['+     92', '0'],
329-
['+     93', '0'],
330-
['+     94', '0'],
331-
['+     95', '0'],
332-
['+     96', '0'],
333-
['+     97', '0'],
334-
['+     98', '0'],
335-
['+     99', '0'],
336-
[
337-
'+     Only 100 properties are shown. React will not log more properties of this object.',
338-
'',
339-
],
235+
['+   buffer', 'Uint8Array(1000)'],
236+
],
237+
tooltipText: 'App',
238+
track: 'Components ⚛',
239+
},
240+
},
241+
end: 31,
242+
start: 21,
243+
},
244+
],
245+
]);
246+
});
247+
248+
// @gate __DEV__ && enableComponentPerformanceTrack
249+
it('shows the type and length of typed arrays instead of enumerating them', async () => {
250+
const App = function App({buffer}) {
251+
Scheduler.unstable_advanceTime(10);
252+
React.useEffect(() => {}, [buffer]);
253+
};
254+
255+
Scheduler.unstable_advanceTime(1);
256+
await act(() => {
257+
ReactNoop.render(<App buffer={new Uint8Array(0)} />);
258+
});
259+
260+
expect(performanceMeasureCalls).toEqual([
261+
[
262+
'Mount',
263+
{
264+
detail: {
265+
devtools: {
266+
color: 'warning',
267+
properties: null,
268+
tooltipText: 'Mount',
269+
track: 'Components ⚛',
270+
},
271+
},
272+
end: 11,
273+
start: 1,
274+
},
275+
],
276+
]);
277+
performanceMeasureCalls.length = 0;
278+
279+
Scheduler.unstable_advanceTime(10);
280+
281+
// A typed array can hold millions of elements. React must not enumerate
282+
// them element-by-element, which would freeze rendering (issue #36200).
283+
await act(() => {
284+
ReactNoop.render(<App buffer={new Float32Array(1000)} />);
285+
});
286+
287+
expect(performanceMeasureCalls).toEqual([
288+
[
289+
'​App',
290+
{
291+
detail: {
292+
devtools: {
293+
color: 'primary-dark',
294+
properties: [
295+
['Changed Props', ''],
296+
['-\xa0buffer', 'Uint8Array(0)'],
297+
['+\xa0buffer', 'Float32Array(1000)'],
340298
],
341299
tooltipText: 'App',
342300
track: 'Components ⚛',

packages/shared/ReactPerformanceTrackProperties.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ export function addObjectToProperties(
6161
indent: number,
6262
prefix: string,
6363
): void {
64+
if (ArrayBuffer.isView(object)) {
65+
// Typed arrays (e.g. Uint8Array, Float32Array) can hold millions of
66+
// elements. Enumerating them with for...in forces the engine to
67+
// materialize a key for every index, which can freeze the page. Their
68+
// contents aren't useful to show here so we skip them. DataView has no
69+
// enumerable properties to show anyway.
70+
return;
71+
}
6472
let addedProperties = 0;
6573
for (const key in object) {
6674
if (hasOwnProperty.call(object, key) && key[0] !== '_') {
@@ -165,6 +173,18 @@ export function addValueToProperties(
165173
// $FlowFixMe[method-unbinding]
166174
const objectToString = Object.prototype.toString.call(value);
167175
let objectName = objectToString.slice(8, objectToString.length - 1);
176+
if (ArrayBuffer.isView(value)) {
177+
// Typed arrays can hold millions of elements. Showing the type and
178+
// length is more useful than enumerating every index (which is also
179+
// prohibitively slow, see addObjectToProperties). DataView is the
180+
// only view without a length; show just its type.
181+
const length = (value as any).length;
182+
desc =
183+
typeof length === 'number'
184+
? objectName + '(' + length + ')'
185+
: objectName;
186+
break;
187+
}
168188
if (objectName === 'Array') {
169189
const array: Array<any> = value as any;
170190
const didTruncate = array.length > OBJECT_WIDTH_LIMIT;

0 commit comments

Comments
 (0)