Skip to content

[slider] Move state to ReactStore - #5592

Draft
flaviendelangle wants to merge 5 commits into
mui:masterfrom
flaviendelangle:slider-store-architecture
Draft

[slider] Move state to ReactStore#5592
flaviendelangle wants to merge 5 commits into
mui:masterfrom
flaviendelangle:slider-store-architecture

Conversation

@flaviendelangle

Copy link
Copy Markdown
Member

Slider currently keeps value state, interaction state, refs, and lifecycle commands across Slider.Root and one large memoized context. This PR moves that lifecycle into a SliderStore based on ReactStore.

The change:

  • moves value updates, commits, interaction state, indicator measurements, label registration, and thumb registration into the store
  • moves refs and callbacks into the non-reactive store context
  • handles controlled values with useControlledProp and callback props with useContextCallback
  • derives the normalized values array through a memoized per-store selector instead of storing a second copy
  • reads mutable store data through selectors so unrelated updates remain isolated
  • keeps render-time root props in a separate React context so descendants receive current values during the same commit
  • adds focused tests for controlled synchronization, current callback props, disabled transitions, atomic interaction updates, and selector isolation

No public API, DOM, or behavior change is intended.

Tradeoff

The standalone Slider entry now includes the ReactStore machinery, which produces a noticeable relative increase for consumers importing only Slider. The full library already includes ReactStore through other components, so its increase is much smaller.

Entry Parsed delta Gzip delta
@base-ui/react +1,694 B (+0.37%) +512 B (+0.34%)
@base-ui/react/slider +5,416 B (+17.06%) +1,608 B (+13.00%)

This trades 1.6 kB gzip for standalone Slider consumers for one owner of the component lifecycle and alignment with the newer store-based components. The whole-library cost is 512 B gzip.

Keeping root props such as disabled outside the synchronized store state is deliberate. useSyncedValues writes in a layout effect, after descendants render. Storing those props only in the store would let descendant callbacks observe the previous render during the same commit.

Verification

  • pnpm test:jsdom Slider --no-watch (337 passed, 82 skipped)

  • pnpm test:chromium Slider --no-watch (417 passed, 2 skipped)

  • pnpm typescript

  • pnpm eslint

  • pnpm prettier

  • pnpm stylelint

  • pnpm extract-error-codes

  • pnpm -F @base-ui/react build

  • pnpm size:snapshot

  • I have followed the PR section of the contributing guide.

@pkg-pr-new

pkg-pr-new Bot commented Aug 28, 2026

Copy link
Copy Markdown

commit: d04085f

@code-infra-dashboard

code-infra-dashboard Bot commented Aug 28, 2026

Copy link
Copy Markdown

Bundle size

Bundle Parsed size Gzip size
@base-ui/react 🔺+2.06KB(+0.45%) 🔺+386B(+0.26%)

Details of bundle changes

Performance

Total duration: 1,113.01 ms +42.50 ms(+4.0%) | Renders: 76 (+0) | Paint: 1,818.32 ms +86.75 ms(+5.0%)

Test Duration Renders
Tabs mount (200 instances) 233.56 ms 🔺+43.59 ms(+22.9%) 3 (+0)
Checkbox mount (500 instances) 78.63 ms 🔺+16.08 ms(+25.7%) 1 (+0)
Popover mount (300 instances) 44.96 ms 🔺+8.11 ms(+22.0%) 1 (+0)
Slider mount (300 instances) 110.94 ms +6.26 ms(+6.0%) 2 (+0)

11 tests within noise — details

Metric alarms

Test Metric Change
Tabs mount (200 instances) bench:paint 🔺 +55.94 ms
Checkbox mount (500 instances) bench:paint 🔺 +23.81 ms
Popover mount (300 instances) bench:paint 🔺 +15.75 ms
Slider mount (300 instances) bench:paint 🔺 +40.16 ms

Check out the code infra dashboard for more information about this PR.

@flaviendelangle flaviendelangle self-assigned this Aug 28, 2026
@flaviendelangle flaviendelangle added component: slider Changes related to the slider component. type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature. labels Aug 28, 2026
@netlify

netlify Bot commented Aug 28, 2026

Copy link
Copy Markdown

Deploy Preview for base-ui ready!

Name Link
🔨 Latest commit d04085f
🔍 Latest deploy log https://app.netlify.com/projects/base-ui/deploys/6a918c8bae16b90008f5eee2
😎 Deploy Preview https://deploy-preview-5592--base-ui.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@flaviendelangle
flaviendelangle marked this pull request as draft August 28, 2026 12:14
flaviendelangle and others added 4 commits August 28, 2026 14:29
- Keep the two most recent `values` entries compared with `Object.is` so
  layout-effect store writes no longer force an extra commit per controlled
  value change, and `NaN` values no longer cause an update loop
- Only write the internal value when uncontrolled
- Flatten interaction state, move refs into the store context, and drop the
  `onValueCommitted` pass-through
- Restore the explanatory comments and document the observer ordering
- Add regression tests for the cache, controlled rejection, field touched
  state, and event target name

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Call `store.x` and `store.context.x` at the use site instead of destructuring
- Drop `readonly` from the store methods to match the other stores
- Keep only the comments the other stores carry

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`useControlledProp` kept the last controlled value in the store after the prop
was removed, so `prop ?? internal` selectors kept reporting a value that was no
longer rendered and commands diverged from the screen. Clear the key so the
internal value takes over, mirroring `useControlled`'s fallback. Also fix the
inverted switch warning and log it once per switch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: slider Changes related to the slider component. type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant