Skip to content

refactor(react): replace prototype-walking and inferred class props#1376

Merged
luwes merged 3 commits into
mainfrom
t3code/899d6507
Apr 20, 2026
Merged

refactor(react): replace prototype-walking and inferred class props#1376
luwes merged 3 commits into
mainfrom
t3code/899d6507

Conversation

@mihar-22
Copy link
Copy Markdown
Member

@mihar-22 mihar-22 commented Apr 20, 2026

Summary

  • Fixed runtime perf — replaced prototype-walking prop discovery (mediaProps) with explicit per-property dirty checks, and memoized the attach/detach ref callback via useAttachMedia hook
  • Removed problematic propsInferClassProps exposed every settable property on the media class (e.g. currentTime, volume, playbackRate) as React props, but React has no two-way binding — setting these during render would fight with the browser's own state. Now only props that are genuine configuration (like src, preload, config, debug) are exposed.

Changes

  • Add useAttachMedia hook wrapping MediaEngineHost.attach/detach in a stable useCallback ref callback
  • Replace mediaProps runtime prop spreading with explicit destructuring and per-property dirty checks
  • Define explicit Partial<Pick<...>> media prop interfaces per component, using Omit<..., keyof MediaProps> to resolve HTML attribute conflicts
  • Delete attach-media-element.ts, media-props.ts, and InferClassProps + helpers from @videojs/utils

Testing

  • pnpm typecheck — passes
  • pnpm -F @videojs/react test — 200 tests pass
  • pnpm -F @videojs/react build — builds successfully
  • pnpm check:workspace — 6/6 checks pass

Note

Medium Risk
Touches the React wrappers’ prop-to-media synchronization and ref attachment paths, which can subtly change when/which media properties are applied. Risk is mitigated by narrowing to explicit config props, but regressions could show up as missing/ignored props or changed defaulting behavior.

Overview
Refactors the React media components (DashVideo, HlsVideo, NativeHlsVideo, SimpleHlsVideo, MuxVideo, MuxAudio) to stop prototype-walking and auto-exposing all writable media fields as React props, and instead sync only explicit configuration props to the underlying media instances via a new useSyncProps helper.

Replaces the old attachMediaElement ref helper with a memoized useAttachMedia hook, and introduces explicit *MediaProps + *DefaultProps exports in core/SPF media hosts (Dash/HLS/NativeHLS/Castable/Mux/SPF) to centralize default values and clarify which properties are supported as controllable props. Removes media-props.ts, attach-media-element.ts, and the InferClassProps utility type from @videojs/utils.

Reviewed by Cursor Bugbot for commit 762fcdc. Bugbot is set up for automated code reviews on this repo. Configure here.

…prop handling

Replace `attachMediaElement` and `mediaProps` utilities with a `useAttachMedia`
hook and explicit per-property synchronization in each media component. This
removes `InferClassProps` from `@videojs/utils` and eliminates runtime prototype
walking, giving each component a statically-typed props interface with proper
`Omit` to avoid conflicts with HTML attributes.
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 20, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
v10-sandbox Ready Ready Preview, Comment Apr 20, 2026 8:57am

Request Review

@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 20, 2026

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit 762fcdc
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/69e5ea582814c10008c8b413
😎 Deploy Preview https://deploy-preview-1376--vjs10-site.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

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

@mihar-22 mihar-22 changed the title refactor(react)!: replace dynamic media prop spreading with explicit prop handling refactor(react): replace dynamic media prop spreading with explicit prop handling Apr 20, 2026
@mihar-22 mihar-22 requested a review from luwes April 20, 2026 06:53
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 20, 2026

📦 Bundle Size Report

🎨 @videojs/html — no changes
Presets (7)
Entry Size
/video (default) 28.45 kB
/video (default + hls) 160.72 kB
/video (minimal) 25.98 kB
/video (minimal + hls) 158.21 kB
/audio (default) 26.36 kB
/audio (minimal) 23.95 kB
/background 4.15 kB
Media (8)
Entry Size
/media/background-video 1.04 kB
/media/container 1.73 kB
/media/dash-video 236.54 kB
/media/hls-video 133.43 kB
/media/mux-audio 159.52 kB
/media/mux-video 159.52 kB
/media/native-hls-video 3.51 kB
/media/simple-hls-video 15.80 kB
Players (3)
Entry Size
/video/player 6.98 kB
/audio/player 5.06 kB
/background/player 3.86 kB
Skins (17)
Entry Type Size
/video/minimal-skin.css css 3.50 kB
/video/skin.css css 3.52 kB
/video/minimal-skin js 25.95 kB
/video/minimal-skin.tailwind js 26.17 kB
/video/skin js 28.50 kB
/video/skin.tailwind js 28.56 kB
/audio/minimal-skin.css css 2.53 kB
/audio/skin.css css 2.50 kB
/audio/minimal-skin js 23.95 kB
/audio/minimal-skin.tailwind js 24.13 kB
/audio/skin js 26.35 kB
/audio/skin.tailwind js 26.51 kB
/background/skin.css css 117 B
/background/skin js 1.14 kB
/base.css css 157 B
/shared.css css 88 B
/skin-element js 1.35 kB
UI Components (25)
Entry Size
/ui/alert-dialog 981 B
/ui/alert-dialog-close 429 B
/ui/alert-dialog-description 375 B
/ui/alert-dialog-title 353 B
/ui/buffering-indicator 2.24 kB
/ui/captions-button 2.55 kB
/ui/cast-button 2.49 kB
/ui/compounds 3.89 kB
/ui/controls 2.19 kB
/ui/error-dialog 2.90 kB
/ui/fullscreen-button 2.54 kB
/ui/hotkey 2.53 kB
/ui/mute-button 2.51 kB
/ui/pip-button 2.52 kB
/ui/play-button 2.55 kB
/ui/playback-rate-button 2.54 kB
/ui/popover 1.77 kB
/ui/poster 2.05 kB
/ui/seek-button 2.54 kB
/ui/slider 1.44 kB
/ui/thumbnail 2.59 kB
/ui/time 2.41 kB
/ui/time-slider 3.84 kB
/ui/tooltip 1.99 kB
/ui/volume-slider 3.22 kB

Sizes are marginal over the root entry point.

⚛️ @videojs/react — no changes
Presets (7)
Entry Size
/video (default) 23.28 kB
/video (default + hls) 154.30 kB
/video (minimal) 20.90 kB
/video (minimal + hls) 151.94 kB
/audio (default) 18.86 kB
/audio (minimal) 17.38 kB
/background 755 B
Media (7)
Entry Size
/media/background-video 575 B
/media/dash-video 235.04 kB
/media/hls-video 132.07 kB
/media/mux-audio 158.25 kB
/media/mux-video 158.16 kB
/media/native-hls-video 2.01 kB
/media/simple-hls-video 14.36 kB
Skins (14)
Entry Type Size
/video/minimal-skin.css css 3.43 kB
/video/skin.css css 3.45 kB
/video/minimal-skin js 20.80 kB
/video/minimal-skin.tailwind js 24.29 kB
/video/skin js 23.17 kB
/video/skin.tailwind js 24.41 kB
/audio/minimal-skin.css css 2.43 kB
/audio/skin.css css 2.39 kB
/audio/minimal-skin js 17.30 kB
/audio/minimal-skin.tailwind js 19.79 kB
/audio/skin js 18.77 kB
/audio/skin.tailwind js 19.80 kB
/background/skin.css css 90 B
/background/skin js 272 B
UI Components (20)
Entry Size
/ui/alert-dialog 1.14 kB
/ui/buffering-indicator 1.25 kB
/ui/captions-button 1.95 kB
/ui/cast-button 1.90 kB
/ui/controls 1.35 kB
/ui/error-dialog 1.74 kB
/ui/fullscreen-button 1.93 kB
/ui/mute-button 1.96 kB
/ui/pip-button 1.91 kB
/ui/play-button 1.92 kB
/ui/playback-rate-button 1.96 kB
/ui/popover 1.94 kB
/ui/poster 1.20 kB
/ui/seek-button 1.96 kB
/ui/slider 2.67 kB
/ui/thumbnail 1.50 kB
/ui/time 2.08 kB
/ui/time-slider 2.21 kB
/ui/tooltip 2.20 kB
/ui/volume-slider 2.27 kB

Sizes are marginal over the root entry point.

🧩 @videojs/core — no changes
Entries (9)
Entry Size
. 4.89 kB
/dom 11.62 kB
/dom/media/castable 4.05 kB
/dom/media/custom-media-element 1.90 kB
/dom/media/dash 234.13 kB
/dom/media/hls 131.46 kB
/dom/media/mux 157.71 kB
/dom/media/native-hls 1.35 kB
/dom/media/simple-hls 13.73 kB
🏷️ @videojs/element — no changes
Entries (2)
Entry Size
. 999 B
/context 943 B
📦 @videojs/store — no changes
Entries (3)
Entry Size
. 1.39 kB
/html 695 B
/react 360 B
🔧 @videojs/utils — no changes
Entries (10)
Entry Size
/array 104 B
/dom 1.92 kB
/events 319 B
/function 327 B
/object 275 B
/predicate 265 B
/string 148 B
/style 190 B
/time 478 B
/number 158 B
📦 @videojs/spf — no changes
Entries (3)
Entry Size
. 40 B
/dom 13.33 kB
/playback-engine 13.24 kB

ℹ️ How to interpret

All sizes are standalone totals (minified + brotli).

Icon Meaning
No change
🔺 Increased ≤ 10%
🔴 Increased > 10%
🔽 Decreased
🆕 New (no baseline)

Run pnpm size locally to check current sizes.

Comment thread packages/react/src/media/hls-video/index.tsx Outdated
Comment thread packages/react/src/media/mux-video/index.tsx Outdated
…useSyncProps hook

Each media class/mixin now exports a Props interface and defaultProps constant.
React components use a generic useSyncProps hook that dirty-checks against
defaults, replacing per-property boilerplate with a single call.

Also renames CastableMediaProps → CastableMedia (full mixin shape),
CastableMediaBase → CastableMediaHost, CastableMediaSuperclass → CastableMediaHostConstructor,
and introduces CastableMediaProps as the settable-only props interface.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 056d71e. Configure here.

Comment thread packages/react/src/utils/use-sync-props.ts Outdated
Comment thread packages/core/src/dom/media/hls/index.ts
… default

- Replace ?? with isUndefined() so null passes through to setters (e.g. castCustomData)
- Spread hlsMediaDefaultProps.config in field initializer to avoid shared reference

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mihar-22 mihar-22 changed the title refactor(react): replace dynamic media prop spreading with explicit prop handling refactor(react): replace prototype-walking and inferred class props Apr 20, 2026
Copy link
Copy Markdown
Collaborator

@luwes luwes left a comment

Choose a reason for hiding this comment

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

LGTM thanks!

@luwes luwes merged commit 2f72120 into main Apr 20, 2026
25 checks passed
@luwes luwes deleted the t3code/899d6507 branch April 20, 2026 16:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants