Skip to content

refactor(react-overflow,priority-overflow): pure manager + strict-mode-safe lifecycle#36262

Merged
bsunderhus merged 7 commits into
microsoft:masterfrom
bsunderhus:react-overflow/pr1-strict-mode-lifecycle
May 29, 2026
Merged

refactor(react-overflow,priority-overflow): pure manager + strict-mode-safe lifecycle#36262
bsunderhus merged 7 commits into
microsoft:masterfrom
bsunderhus:react-overflow/pr1-strict-mode-lifecycle

Conversation

@bsunderhus
Copy link
Copy Markdown
Contributor

@bsunderhus bsunderhus commented May 28, 2026

Summary

PR 1 of 3 in a stack that refactors the priority-overflow manager and the react-overflow container hook for strict-mode safety, moves the overflow snapshot out of the <Overflow> container, and fixes the overflow snapshot on the first painted frame.

Stack

  1. This PR — strict-mode-safe lifecycle.
  2. react-overflow/pr2-subscribe-model — manager exposes getSnapshot/subscribe; auxiliary hooks subscribe directly; <Overflow> drops its useState.
  3. react-overflow/pr3-first-paint — overflow snapshot is correct on the first painted frame.

priority-overflow — additions only, no API modifications

OverflowManager's diff vs master is exclusively additions:

  • createOverflowManager(initialOptions?: Partial<ObserveOptions>) — new optional parameter. Existing zero-arg callers still compile.
  • observe(container, options?)options widened from required to optional. Existing 2-arg callers still compile; 1-arg call now allowed for consumers that already pre-populated options.
  • setOptions: (options: Partial<ObserveOptions>) => void — new method that updates engine options without rebuilding the manager.

All other methods (addItem, removeItem, addOverflowMenu, disconnect, forceUpdate, …) keep their master signatures and runtime behaviour, including parameter names.

react-overflow

  • The manager is created via a useRef lazy initializer so it's instantiated once per <Overflow> instance and React renders stay pure — no useMemo([]) + eslint-disable react-hooks/exhaustive-deps.
  • Observation starts in a single useIsomorphicLayoutEffect; the effect cleanup calls manager.disconnect().
  • Option changes propagate via manager.setOptions(observeOptions) in a separate layout effect, instead of rebuilding the manager.

Test plan

  • yarn nx run priority-overflow:test
  • yarn nx run priority-overflow:lint
  • yarn nx run priority-overflow:type-check
  • yarn nx run react-overflow:test
  • yarn nx run react-overflow:lint
  • yarn nx run react-overflow:type-check
  • CI green

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 28, 2026

📊 Bundle size report

Package & Exports Baseline (minified/GZIP) PR Change
priority-overflow
createOverflowManager
4.584 kB
1.873 kB
4.988 kB
1.998 kB
404 B
125 B
react-charts
AreaChart
401.77 kB
125.325 kB
402.425 kB
125.477 kB
655 B
152 B
react-charts
DeclarativeChart
752.718 kB
219.767 kB
753.371 kB
219.875 kB
653 B
108 B
react-charts
DonutChart
312.178 kB
96.155 kB
312.833 kB
96.255 kB
655 B
100 B
react-charts
FunnelChart
303.731 kB
92.988 kB
304.387 kB
93.104 kB
656 B
116 B
react-charts
GanttChart
384.859 kB
119.806 kB
385.514 kB
119.91 kB
655 B
104 B
react-charts
GaugeChart
311.611 kB
95.553 kB
312.266 kB
95.651 kB
655 B
98 B
react-charts
GroupedVerticalBarChart
392.728 kB
122.511 kB
393.384 kB
122.619 kB
656 B
108 B
react-charts
HeatMapChart
386.946 kB
120.706 kB
387.601 kB
120.863 kB
655 B
157 B
react-charts
HorizontalBarChart
291.905 kB
88.605 kB
292.56 kB
88.697 kB
655 B
92 B
react-charts
Legends
231.58 kB
69.461 kB
232.229 kB
69.577 kB
649 B
116 B
react-charts
LineChart
413.088 kB
128.366 kB
413.743 kB
128.465 kB
655 B
99 B
react-charts
PolarChart
340.572 kB
106.052 kB
341.227 kB
106.142 kB
655 B
90 B
react-charts
ScatterChart
392.471 kB
122.487 kB
393.126 kB
122.588 kB
655 B
101 B
react-charts
VerticalBarChart
429.215 kB
127.37 kB
429.87 kB
127.494 kB
655 B
124 B
react-charts
VerticalStackedBarChart
398.933 kB
123.933 kB
399.588 kB
124.051 kB
655 B
118 B
react-components
react-components: entire library
1.293 MB
324.811 kB
1.294 MB
324.918 kB
655 B
107 B
react-overflow
hooks only
11.966 kB
4.565 kB
12.519 kB
4.642 kB
553 B
77 B
Unchanged fixtures
Package & Exports Size (minified/GZIP)
react-breadcrumb
@fluentui/react-breadcrumb - package
102.926 kB
28.76 kB
react-charts
HorizontalBarChartWithAxis
63 B
83 B
react-charts
SankeyChart
211.914 kB
67.836 kB
react-charts
Sparkline
80.503 kB
26.644 kB
react-components
react-components: Button, FluentProvider & webLightTheme
66.328 kB
19.02 kB
react-components
react-components: Accordion, Button, FluentProvider, Image, Menu, Popover
226.19 kB
67.909 kB
react-components
react-components: FluentProvider & webLightTheme
39.525 kB
13.113 kB
react-headless-components-preview
react-headless-components-preview: entire library
198.139 kB
56.549 kB
react-portal-compat
PortalCompatProvider
5.567 kB
2.237 kB
react-timepicker-compat
TimePicker
104.049 kB
34.748 kB
🤖 This report was generated against 35720ad3c847dcf7877dfaab72ad295b76c47a08

@github-actions
Copy link
Copy Markdown

Pull request demo site: URL

Comment thread packages/react-components/react-overflow/library/src/useOverflowContainer.ts Outdated
Comment thread packages/react-components/react-overflow/library/src/useOverflowContainer.ts Outdated
Comment thread packages/react-components/priority-overflow/src/overflowManager.ts Outdated
Comment thread change/@fluentui-react-overflow-pr1-strict-mode.json Outdated
Comment thread packages/react-components/priority-overflow/src/overflowManager.ts Outdated
Comment thread packages/react-components/priority-overflow/src/overflowManager.ts
Comment thread packages/react-components/priority-overflow/src/overflowManager.ts Outdated
Comment thread packages/react-components/react-overflow/library/src/useOverflowContainer.ts Outdated
Comment thread change/@fluentui-priority-overflow-pr1-strict-mode.json
bsunderhus and others added 2 commits May 28, 2026 16:20
…e and lifecycle strict-mode safe

Restructure the priority-overflow manager and the react-overflow container hook so
that manager creation is render-pure and the observation lifecycle is colocated.

priority-overflow:
- createOverflowManager now accepts initialOptions and merges them over defaults
- new setOptions method updates engine options without re-creating the manager
- observe(container) returns a cleanup function that stops observation only
  (item, divider, and overflow menu registrations are no longer torn down)
- disconnect is preserved as a shortcut for the latest observe cleanup
- introduce OverflowManagerOptions type alias and complete tsdoc on the surface

react-overflow:
- create the manager via useMemo so React renders stay pure
- start observation in a single useIsomorphicLayoutEffect and return the
  observe cleanup as the effect cleanup
- propagate option changes through setOptions instead of rebuilding the manager
- ref-guard the overflow menu and divider deregistration so strict-mode double
  invocation does not unregister a freshly mounted element

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bsunderhus bsunderhus force-pushed the react-overflow/pr1-strict-mode-lifecycle branch from 538ecc0 to ae93c45 Compare May 28, 2026 14:27
bsunderhus and others added 5 commits May 28, 2026 17:36
- useOverflowContainer: drop redundant destructure defaults; the manager's
  DEFAULT_OPTIONS is the single source of truth. Adjust the test accordingly.
- useOverflowContainer: switch manager creation to a useRef lazy initializer
  with a localized react-hooks/refs disable.
- useOverflowContainer: setOptions propagation moves to a layout effect so
  option changes land before paint.
- useOverflowContainer: drop the ref-guarded cleanups on registerOverflowMenu
  and registerDivider; in normal React flow the previous cleanup always runs
  before the next registration.
- DEFAULT_OPTIONS no-op callbacks use a commented block to satisfy
  no-empty-function.
- Shorten the priority-overflow change-file comment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removing the destructure defaults in useOverflowContainer caused undefined
fields to spread over the manager's DEFAULT_OPTIONS, leaving runtime values
like options.padding as undefined. processOverflowItems then computed
availableSize as NaN, so no items overflowed and the menu never rendered.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…API surface

Restore the original observe / disconnect contract on OverflowManager so the
@internal interface keeps its existing signatures. observe's options parameter
becomes optional (backward-compatible widening) so the React layer can drive
options through createOverflowManager and setOptions instead of re-passing
them at every observation. disconnect goes back to its full reset behaviour.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…flow manager

Switch useOverflowContainer's manager storage from a useMemo with empty
dependencies to a useRef with an in-render lazy initializer. The ref pattern
is the canonical "create once per instance" idiom documented by React for
this exact use case, and it lets every dependent useCallback /
useIsomorphicLayoutEffect drop manager from its deps array.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-positive dispatch

Adds a regression test covering setOptions called with a partial whose values
match the manager's current state. The previous-capture comparison continues to
short-circuit correctly; this test pins that behaviour so a future refactor that
compares the live options against the partial directly cannot silently regress
into spurious dispatches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bsunderhus bsunderhus merged commit d7cec2e into microsoft:master May 29, 2026
12 checks passed
@bsunderhus bsunderhus deleted the react-overflow/pr1-strict-mode-lifecycle branch May 29, 2026 10:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants