Skip to content

Commit c0acd03

Browse files
authored
revert: remove ParentRerender stress test
1 parent 3eb5a4a commit c0acd03

File tree

1 file changed

+0
-139
lines changed

1 file changed

+0
-139
lines changed

packages/react/src/ActionList/ActionList.stress.dev.stories.tsx

Lines changed: 0 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import type {ComponentProps} from '../utils/types'
33
import {StressTest} from '../utils/StressTest'
44
import {TableIcon} from '@primer/octicons-react'
55
import {ActionList} from '.'
6-
import React, {useState, useEffect, useRef, useCallback, memo} from 'react'
7-
import afterFrame from 'afterframe'
86

97
export default {
108
title: 'StressTests/Components/ActionList',
@@ -50,140 +48,3 @@ export const SingleSelect = () => {
5048
/>
5149
)
5250
}
53-
54-
export const ParentRerender = () => {
55-
return <ContextMemoizationBenchmark />
56-
}
57-
58-
/**
59-
* This benchmark isolates the effect of context value memoization.
60-
*
61-
* Setup: A parent component holds a counter that increments 100 times.
62-
* The ActionList is rendered via a memoized child component (MemoizedList),
63-
* so React only re-renders it if props or context change.
64-
*
65-
* - Without useMemo on ListContext/ItemContext: the context values are new
66-
* objects every render, so React re-renders all context consumers (every
67-
* Description, LeadingVisual, TrailingVisual, Selection in every Item).
68-
* - With useMemo: context values are referentially stable, React bails out
69-
* of re-rendering the consumers entirely.
70-
*/
71-
72-
const listItems = Array.from({length: 100}, (_, i) => ({
73-
name: `Project ${i + 1}`,
74-
scope: `Scope ${i + 1}`,
75-
}))
76-
77-
// Memoized list component: only re-renders when props change or context forces it
78-
const MemoizedList = memo(function MemoizedList() {
79-
return (
80-
<ActionList selectionVariant="single" showDividers role="menu" aria-label="Project">
81-
{listItems.map((project, index) => (
82-
<ActionList.Item key={index} role="menuitemradio" selected={index === 0} aria-checked={index === 0}>
83-
<ActionList.LeadingVisual>
84-
<TableIcon />
85-
</ActionList.LeadingVisual>
86-
{project.name}
87-
<ActionList.Description variant="block">{project.scope}</ActionList.Description>
88-
</ActionList.Item>
89-
))}
90-
</ActionList>
91-
)
92-
})
93-
94-
function ContextMemoizationBenchmark() {
95-
const totalIterations = 100
96-
const [count, setCount] = useState(0)
97-
const [running, setRunning] = useState(false)
98-
const [results, setResults] = useState<{median: number; average: number; min: number; max: number} | null>(null)
99-
const observerRef = useRef<{observer: PerformanceObserver; data: number[]} | null>(null)
100-
101-
useEffect(() => {
102-
const duration: number[] = []
103-
const obs = new PerformanceObserver(list => {
104-
for (const entry of list.getEntries()) {
105-
if (entry.entryType === 'measure' && entry.name === 'ctx-rerender') {
106-
duration.push(entry.duration)
107-
}
108-
}
109-
})
110-
obs.observe({entryTypes: ['measure']})
111-
observerRef.current = {data: duration, observer: obs}
112-
return () => obs.disconnect()
113-
}, [])
114-
115-
const start = useCallback(() => {
116-
if (observerRef.current) observerRef.current.data.length = 0
117-
setResults(null)
118-
setRunning(true)
119-
setCount(0)
120-
121-
let i = 0
122-
const interval = setInterval(() => {
123-
if (i < totalIterations - 1) {
124-
performance.mark('ctx-start')
125-
setCount(c => c + 1)
126-
afterFrame(() => {
127-
performance.mark('ctx-end')
128-
performance.measure('ctx-rerender', 'ctx-start', 'ctx-end')
129-
})
130-
i++
131-
} else {
132-
clearInterval(interval)
133-
setTimeout(() => {
134-
const durations = observerRef.current?.data ?? []
135-
const sorted = [...durations].sort((a, b) => a - b)
136-
setResults({
137-
median: sorted[Math.floor(sorted.length / 2)] ?? 0,
138-
average: durations.reduce((a, b) => a + b, 0) / durations.length,
139-
min: Math.min(...durations),
140-
max: Math.max(...durations),
141-
})
142-
setRunning(false)
143-
}, 100)
144-
}
145-
}, 10)
146-
}, [])
147-
148-
return (
149-
<div style={{padding: 16}}>
150-
<div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16}}>
151-
<div>
152-
<strong>ActionList Context Memoization</strong>
153-
<p style={{color: '#656d76', margin: '4px 0'}}>
154-
Parent re-renders 100x with a counter. ActionList is memoized, so only context changes trigger item
155-
re-renders. Tests whether ListContext/ItemContext values are stable.
156-
</p>
157-
</div>
158-
<button
159-
type="button"
160-
onClick={start}
161-
disabled={running}
162-
style={{
163-
padding: '8px 16px',
164-
background: running ? '#ccc' : '#1a7f37',
165-
color: 'white',
166-
border: 'none',
167-
borderRadius: 6,
168-
cursor: running ? 'default' : 'pointer',
169-
}}
170-
>
171-
{running ? `Running ${count}/${totalIterations}...` : results ? 'Re-run' : 'Start'}
172-
</button>
173-
</div>
174-
175-
{/* The counter forces this component to re-render, but MemoizedList should bail out if context is stable */}
176-
<div style={{display: 'none'}}>Counter: {count}</div>
177-
<MemoizedList />
178-
179-
{results && (
180-
<div style={{marginTop: 16, fontFamily: 'monospace', fontSize: 14}}>
181-
<strong>Median: {results.median.toFixed(2)}ms</strong>
182-
{' | '}Average: {results.average.toFixed(2)}ms{' | '}
183-
Min: {results.min.toFixed(2)}ms{' | '}
184-
Max: {results.max.toFixed(2)}ms
185-
</div>
186-
)}
187-
</div>
188-
)
189-
}

0 commit comments

Comments
 (0)