Skip to content

refactor(react-overflow,priority-overflow): subscribe model removes intermediate state from <Overflow>#36263

Open
bsunderhus wants to merge 3 commits into
microsoft:masterfrom
bsunderhus:react-overflow/pr2-subscribe-model
Open

refactor(react-overflow,priority-overflow): subscribe model removes intermediate state from <Overflow>#36263
bsunderhus wants to merge 3 commits into
microsoft:masterfrom
bsunderhus:react-overflow/pr2-subscribe-model

Conversation

@bsunderhus
Copy link
Copy Markdown
Contributor

@bsunderhus bsunderhus commented May 28, 2026

Summary

PR 2 of 3. Depends on PR 1 (#36262, react-overflow/pr1-strict-mode-lifecycle). Do not merge until PR 1 lands — the "Files changed" view here will show PR 1's diff cumulatively until then; GitHub narrows it automatically once PR 1 merges.

Moves the overflow snapshot out of the <Overflow> container and into the manager itself, so each auxiliary hook subscribes directly to the manager without forcing intermediate React state.

Stack

  1. react-overflow/pr1-strict-mode-lifecycle (refactor(react-overflow,priority-overflow): pure manager + strict-mode-safe lifecycle #36262) — strict-mode-safe lifecycle.
  2. This PR — subscribe model.
  3. react-overflow/pr3-first-paint — first-paint correctness.

priority-overflow

  • The manager caches the latest snapshot and exposes getSnapshot / subscribe.
  • dispatchOverflowUpdate goes through a single takeSnapshot helper that updates the cache and fans out to listeners.
  • The observe cleanup resets the snapshot so subscribers see an empty state.

react-overflow

  • OverflowContextValue exposes getSnapshot / subscribe directly from the manager.
  • The context drops @fluentui/react-context-selector and uses plain React.createContext; useOverflowContext keeps the selector overload for backward compatibility.
  • itemVisibility, groupVisibility, and hasOverflow on the context are kept as deprecated, always-empty placeholders so existing external consumers still type-check.
  • New useOverflowSnapshot subscribes via useState + useIsomorphicLayoutEffect.
  • useOverflowCount, useIsOverflowItemVisible, useIsOverflowGroupVisible, and useOverflowVisibility are rewritten on top of useOverflowSnapshot.
  • <Overflow> no longer holds a useState; onOverflowChange callers receive the same OnOverflowChangeData shape, now derived from the snapshot.
  • @fluentui/react-context-selector removed from package dependencies.

Test plan

  • yarn nx run priority-overflow:test / react-overflow:test
  • yarn nx run priority-overflow:lint / react-overflow:lint
  • yarn nx run react-overflow:type-check
  • CI green
  • After PR 1 merges: mark this PR ready for review

@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.988 kB
1.998 kB
5.209 kB
2.071 kB
221 B
73 B
react-charts
AreaChart
402.425 kB
125.477 kB
403.203 kB
125.661 kB
778 B
184 B
react-charts
DeclarativeChart
753.371 kB
219.875 kB
754.149 kB
220.02 kB
778 B
145 B
react-charts
DonutChart
312.833 kB
96.255 kB
313.611 kB
96.419 kB
778 B
164 B
react-charts
FunnelChart
304.387 kB
93.104 kB
305.165 kB
93.245 kB
778 B
141 B
react-charts
GanttChart
385.514 kB
119.91 kB
386.297 kB
120.085 kB
783 B
175 B
react-charts
GaugeChart
312.266 kB
95.651 kB
313.044 kB
95.817 kB
778 B
166 B
react-charts
GroupedVerticalBarChart
393.384 kB
122.619 kB
394.168 kB
122.791 kB
784 B
172 B
react-charts
HeatMapChart
387.601 kB
120.863 kB
388.379 kB
121.056 kB
778 B
193 B
react-charts
HorizontalBarChart
292.56 kB
88.697 kB
293.338 kB
88.883 kB
778 B
186 B
react-charts
Legends
232.229 kB
69.577 kB
233.012 kB
69.791 kB
783 B
214 B
react-charts
LineChart
413.743 kB
128.465 kB
414.527 kB
128.656 kB
784 B
191 B
react-charts
PolarChart
341.227 kB
106.142 kB
342.011 kB
106.34 kB
784 B
198 B
react-charts
ScatterChart
393.126 kB
122.588 kB
393.91 kB
122.777 kB
784 B
189 B
react-charts
VerticalBarChart
429.87 kB
127.494 kB
430.648 kB
127.675 kB
778 B
181 B
react-charts
VerticalStackedBarChart
399.588 kB
124.051 kB
400.372 kB
124.253 kB
784 B
202 B
react-components
react-components: entire library
1.294 MB
324.918 kB
1.295 MB
325.116 kB
851 B
198 B
react-overflow
hooks only
12.519 kB
4.642 kB
8.873 kB
3.127 kB
-3.646 kB
-1.515 kB
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 d7cec2eece3668834f65d8f60f60e9b8004664f1

@github-actions
Copy link
Copy Markdown

Pull request demo site: URL

@@ -0,0 +1,7 @@
{
Copy link
Copy Markdown

@github-actions github-actions Bot May 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🕵🏾‍♀️ visual changes to review in the Visual Change Report

vr-tests-react-components/Charts-DonutChart 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Charts-DonutChart.Dynamic.default.chromium.png 33361 Changed
vr-tests-react-components/Positioning 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Positioning.Positioning end.chromium.png 615 Changed
vr-tests-react-components/Positioning.Positioning end.updated 2 times.chromium.png 749 Changed
vr-tests-react-components/ProgressBar converged 3 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - High Contrast.default.chromium.png 41 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness.default.chromium.png 121 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - Dark Mode.default.chromium.png 49 Changed
vr-tests-react-components/TagPicker 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/TagPicker.disabled - RTL.chromium.png 635 Changed
vr-tests-react-components/TagPicker.disabled.chromium.png 677 Changed

There were 2 duplicate changes discarded. Check the build logs for more information.

@bsunderhus bsunderhus force-pushed the react-overflow/pr2-subscribe-model branch 3 times, most recently from 61974e9 to ef9fd48 Compare May 28, 2026 17:46
bsunderhus and others added 3 commits May 29, 2026 12:57
…ntermediate state from Overflow container

The Overflow container previously held the overflow snapshot in component state
and re-rendered every consumer through context selector when it changed. This
forced consumers to opt out via memoization and made first-paint sequencing
fragile. Move the snapshot into the manager and let each hook subscribe
directly.

priority-overflow:
- the manager caches the latest snapshot and exposes getSnapshot/subscribe
- dispatchOverflowUpdate goes through takeSnapshot, which fans out to listeners
- the observe cleanup resets the snapshot so subscribers see an empty state

react-overflow:
- OverflowContextValue exposes getSnapshot/subscribe directly from the manager
- the context drops react-context-selector and uses plain React.createContext;
  useOverflowContext keeps the selector overload for backward compatibility
- the existing itemVisibility, groupVisibility and hasOverflow fields on the
  context are kept but marked deprecated — they are now always empty
- new useOverflowSnapshot hook subscribes via useState + useIsomorphicLayoutEffect
- useOverflowCount, useIsOverflowItemVisible, useIsOverflowGroupVisible and
  useOverflowVisibility are rewritten on top of useOverflowSnapshot
- Overflow.tsx no longer keeps a useState; onOverflowChange callers receive the
  same OnOverflowChangeData shape derived from the snapshot
- drop the @fluentui/react-context-selector dependency from the package

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- overflowContext.ts: replace the inline `() => () => null` / `() => null`
  default no-ops on the context value with references to a local `noop` const
  (block-form body).
- useOverflowContainer.ts: `defaultSubscribe` now returns the same shared
  `noop` rather than re-allocating a fresh placeholder per call.

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/pr2-subscribe-model branch from ef9fd48 to de5968f Compare May 29, 2026 11:05

// @internal (undocumented)
export interface UseOverflowContainerReturn<TElement extends HTMLElement> extends Pick<OverflowContextValue, 'registerItem' | 'updateOverflow' | 'registerOverflowMenu' | 'registerDivider'> {
export interface UseOverflowContainerReturn<TElement extends HTMLElement> extends Pick<OverflowContextValue, 'registerItem' | 'updateOverflow' | 'registerOverflowMenu' | 'registerDivider' | 'getSnapshot' | 'subscribe'> {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Breaking change? @layershifter

Comment on lines +71 to +77
getSnapshot: () => OverflowEventPayload;
observe: (container: HTMLElement, options?: ObserveOptions) => void;
removeDivider: (groupId: string) => void;
removeItem: (itemId: string) => void;
removeOverflowMenu: () => void;
setOptions: (options: Partial<ObserveOptions>) => void;
subscribe: (listener: () => void) => () => void;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Breaking change? @layershifter

@bsunderhus bsunderhus marked this pull request as ready for review May 29, 2026 11:42
@bsunderhus bsunderhus requested a review from a team as a code owner May 29, 2026 11:42
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.

1 participant