Skip to content

fix(react): memoize Provider context value#1787

Merged
mihar-22 merged 2 commits into
mainfrom
fix/1541-provider-context-value
Jul 1, 2026
Merged

fix(react): memoize Provider context value#1787
mihar-22 merged 2 commits into
mainfrom
fix/1541-provider-context-value

Conversation

@mihar-22

@mihar-22 mihar-22 commented Jul 1, 2026

Copy link
Copy Markdown
Member

Replaces #1541 because the source fork has maintainer edits disabled, so the review cleanup could not be pushed there.

Related to #1296.

What changed

  • Memoizes the createPlayer().Provider context value so parent re-renders do not rebuild the value object unnecessarily.
  • Adds a regression test that verifies the context value identity stays stable across parent re-renders.
  • Applies the requested comment cleanup from fix(react): memoize Provider context value #1541.

Verification

  • pnpm --filter @videojs/react... build
  • pnpm -F @videojs/react test src/player/tests/create-player.test.tsx
  • pnpm lint:fix:file packages/react/src/player/create-player.tsx packages/react/src/player/tests/create-player.test.tsx

Note

Low Risk
Small React Provider change with a focused regression test; no auth, data, or API surface changes.

Overview
Fixes unnecessary context updates when the parent re-renders but player state is unchanged (#1296).

The createPlayer().Provider now builds its context value with useMemo, keyed on store, media, container, and popupGroup, instead of allocating a new object every render. Consumers that depend on context identity or shallow stability should no longer re-render solely because an ancestor updated.

A regression test forces several parent re-renders and asserts the context value reference stays the same.

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

James and others added 2 commits July 1, 2026 11:52
The `Provider` returned by `createPlayer()` was building a fresh context
value object on every render. Every `useContext(PlayerContext)` consumer
therefore re-rendered on any parent re-render — even when none of the
value's members changed. Combined with callback-ref usage in media
components (`HlsVideo`, `DashVideo`, etc.), this caused the underlying
media to detach and re-attach on every parent state change. The most
visible symptom: `onEnded` fires, parent state updates, the video
rewinds to t=0 and plays again.

Wrap the value object in `useMemo` keyed on `[store, media, container,
popupGroup]`. `setMedia` and `setContainer` are `useState` setters and
are identity-stable; `store` and `popupGroup` are also initialized once
via `useState(() => ...)` and never change for the lifetime of the
Provider, but they're listed in the dep array to satisfy Biome's
`useExhaustiveDependencies`.

Adds a regression test that asserts context value identity is preserved
across forced parent re-renders.

Signed-off-by: James <james@BlainWebDesign.com>
@netlify

netlify Bot commented Jul 1, 2026

Copy link
Copy Markdown

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit bf40bb2
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/6a456245b79c6a00086928bc
😎 Deploy Preview https://deploy-preview-1787--vjs10-site.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.

@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
v10-sandbox Ready Ready Preview, Comment Jul 1, 2026 6:54pm

Request Review

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

🎨 @videojs/html — no changes
Presets (7)
Entry Initial
/video (default) 46.22 kB
/video (default + hls) 185.88 kB
/video (minimal) 46.20 kB
/video (minimal + hls) 185.91 kB
/audio (default) 39.78 kB
/audio (minimal) 38.84 kB
/background 4.20 kB
Media (10)
Entry Initial
/media/background-video 1.14 kB
/media/container 1.72 kB
/media/dash-video 214.72 kB
/media/hlsjs-video 141.49 kB
/media/mux-audio 163.89 kB
/media/mux-video 163.96 kB
/media/native-hls-video 9.05 kB
/media/simple-hls-audio-only 17.15 kB
/media/simple-hls-video 18.72 kB
/media/vimeo-video 12.31 kB
Players (5)
Entry Initial
/video/player 8.22 kB
/audio/player 5.38 kB
/background/player 3.92 kB
/live-video/player 7.64 kB
/live-audio/player 5.39 kB
Skins (30)
Entry Type Initial
/video/minimal-skin.css css 5.65 kB
/video/skin.css css 5.61 kB
/video/minimal-skin js 46.15 kB
/video/minimal-skin.tailwind js 46.92 kB
/video/skin js 46.18 kB
/video/skin.tailwind js 46.96 kB
/audio/minimal-skin.css css 3.93 kB
/audio/skin.css css 3.82 kB
/audio/minimal-skin js 38.95 kB
/audio/minimal-skin.tailwind js 39.36 kB
/audio/skin js 39.78 kB
/audio/skin.tailwind js 40.27 kB
/background/skin.css css 133 B
/background/skin js 1.14 kB
/live-video/minimal-skin.css css 5.65 kB
/live-video/skin.css css 5.61 kB
/live-video/minimal-skin js 45.20 kB
/live-video/minimal-skin.tailwind js 45.73 kB
/live-video/skin js 44.77 kB
/live-video/skin.tailwind js 45.50 kB
/live-audio/minimal-skin.css css 3.93 kB
/live-audio/skin.css css 3.82 kB
/live-audio/minimal-skin js 31.54 kB
/live-audio/minimal-skin.tailwind js 30.98 kB
/live-audio/skin js 32.54 kB
/live-audio/skin.tailwind js 32.01 kB
/global.css css 176 B
/shared.css css 104 B
/tailwind.css css 161 B
/skin-element js 1.45 kB
UI Components (39)
Entry Initial
/ui/airplay-button 2.44 kB
/ui/alert-dialog 2.56 kB
/ui/alert-dialog-close 2.19 kB
/ui/alert-dialog-description 2.17 kB
/ui/alert-dialog-title 2.16 kB
/ui/audio-track-radio-group 2.85 kB
/ui/buffering-indicator 2.27 kB
/ui/captions-button 2.45 kB
/ui/captions-radio-group 2.83 kB
/ui/cast-button 2.41 kB
/ui/compounds 2.88 kB
/ui/controls 2.62 kB
/ui/error-dialog 2.65 kB
/ui/fullscreen-button 2.41 kB
/ui/hotkey 2.30 kB
/ui/menu 2.67 kB
/ui/mute-button 2.41 kB
/ui/pip-button 2.39 kB
/ui/play-button 2.45 kB
/ui/playback-rate-button 2.40 kB
/ui/playback-rate-radio-group 2.80 kB
/ui/popover 2.72 kB
/ui/poster 2.27 kB
/ui/quality-radio-group 2.80 kB
/ui/seek-button 2.34 kB
/ui/seek-indicator 2.37 kB
/ui/seek-indicator-value 446 B
/ui/slider 2.67 kB
/ui/status-announcer 2.30 kB
/ui/status-indicator 2.40 kB
/ui/status-indicator-value 411 B
/ui/thumbnail 2.19 kB
/ui/time 2.64 kB
/ui/time-slider 2.66 kB
/ui/tooltip 2.65 kB
/ui/volume-indicator 2.33 kB
/ui/volume-indicator-fill 416 B
/ui/volume-indicator-value 392 B
/ui/volume-slider 2.67 kB

Sizes are marginal over the root entry point.

⚛️ @videojs/react — no changes
Presets (7)
Entry Initial
/video (default) 38.31 kB
/video (default + hls) 176.58 kB
/video (minimal) 38.65 kB
/video (minimal + hls) 177.24 kB
/audio (default) 31.51 kB
/audio (minimal) 31.60 kB
/background 754 B
Media (9)
Entry Initial
/media/background-video 575 B
/media/dash-video 213.16 kB
/media/hlsjs-video 140.07 kB
/media/mux-audio 162.52 kB
/media/mux-video 162.57 kB
/media/native-hls-video 7.41 kB
/media/simple-hls-audio-only 15.56 kB
/media/simple-hls-video 17.14 kB
/media/vimeo-video 10.58 kB
Skins (27)
Entry Type Initial
/tailwind.css css 161 B
/video/minimal-skin.css css 5.55 kB
/video/skin.css css 5.52 kB
/video/minimal-skin js 38.57 kB
/video/minimal-skin.tailwind js 44.64 kB
/video/skin js 38.21 kB
/video/skin.tailwind js 44.31 kB
/audio/minimal-skin.css css 3.79 kB
/audio/skin.css css 3.68 kB
/audio/minimal-skin js 31.48 kB
/audio/minimal-skin.tailwind js 33.72 kB
/audio/skin js 31.46 kB
/audio/skin.tailwind js 35.63 kB
/background/skin.css css 90 B
/background/skin js 272 B
/live-video/minimal-skin.css css 5.55 kB
/live-video/skin.css css 5.52 kB
/live-video/minimal-skin js 33.37 kB
/live-video/minimal-skin.tailwind js 39.33 kB
/live-video/skin js 33.38 kB
/live-video/skin.tailwind js 39.35 kB
/live-audio/minimal-skin.css css 3.79 kB
/live-audio/skin.css css 3.68 kB
/live-audio/minimal-skin js 22.45 kB
/live-audio/minimal-skin.tailwind js 25.59 kB
/live-audio/skin js 22.52 kB
/live-audio/skin.tailwind js 25.65 kB
UI Components (33)
Entry Initial
/ui/airplay-button 2.20 kB
/ui/alert-dialog 2.16 kB
/ui/audio-track 2.18 kB
/ui/buffering-indicator 2.15 kB
/ui/captions-button 2.24 kB
/ui/captions-radio-group 2.10 kB
/ui/cast-button 2.18 kB
/ui/controls 2.05 kB
/ui/error-dialog 2.25 kB
/ui/fullscreen-button 2.19 kB
/ui/gesture 2.26 kB
/ui/hotkey 2.22 kB
/ui/live-button 2.13 kB
/ui/menu 2.29 kB
/ui/mute-button 2.21 kB
/ui/pip-button 2.20 kB
/ui/play-button 2.22 kB
/ui/playback-rate 2.02 kB
/ui/playback-rate-button 2.21 kB
/ui/popover 2.62 kB
/ui/poster 2.05 kB
/ui/quality 2.14 kB
/ui/seek-button 2.21 kB
/ui/seek-indicator 2.11 kB
/ui/slider 2.28 kB
/ui/status-announcer 2.09 kB
/ui/status-indicator 2.05 kB
/ui/thumbnail 2.03 kB
/ui/time 2.01 kB
/ui/time-slider 2.21 kB
/ui/tooltip 2.54 kB
/ui/volume-indicator 2.10 kB
/ui/volume-slider 2.24 kB

Sizes are marginal over the root entry point.

🧩 @videojs/core — no changes
Entries (14)
Entry Initial
. 9.40 kB
/dom 17.52 kB
/dom/media/custom-media-element 2.09 kB
/dom/media/dash 208.96 kB
/dom/media/google-cast 4.04 kB
/dom/media/hls-js 136.00 kB
/dom/media/media-host 1.25 kB
/dom/media/media-played-ranges 576 B
/dom/media/mux 151.26 kB
/dom/media/native-hls 3.07 kB
/dom/media/simple-hls 16.49 kB
/dom/media/simple-hls-audio-only 14.92 kB
/dom/media/vimeo 9.86 kB
/media/predicate 563 B
🏷️ @videojs/element — no changes
Entries (2)
Entry Initial
. 996 B
/context 943 B
📦 @videojs/store — no changes
Entries (3)
Entry Initial
. 1.39 kB
/html 696 B
/react 360 B
🔧 @videojs/utils — no changes
Entries (10)
Entry Initial
/array 104 B
/dom 2.26 kB
/events 319 B
/function 327 B
/object 275 B
/predicate 265 B
/string 231 B
/style 190 B
/time 478 B
/number 158 B
📦 @videojs/spf — no changes
Entries (4)
Entry Initial
. 4.45 kB
/dom 6.33 kB
/hls 15.37 kB
/background-video 12.85 kB

ℹ️ How to interpret

JS sizes are initial static graph totals (minified + brotli). Lazy dynamic chunks are shown separately when present.

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

Run pnpm size locally to check current initial sizes.

@decepulis decepulis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

looks lgtm to me

@mihar-22 mihar-22 merged commit 6f6657a into main Jul 1, 2026
26 checks passed
@mihar-22 mihar-22 deleted the fix/1541-provider-context-value branch July 1, 2026 18:57
@luwes luwes mentioned this pull request Jul 1, 2026
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