fix(call): rAF batch flush never emptied into state
The real reason every thumbnail card stayed on the placeholder was a
reference-aliasing bug in the flush closure. `const batch = pendingUrls`
captured the same object; `delete (pendingUrls)[k]` for each key then
emptied `batch` too, because they were the same reference. By the time
`setThumbnailUrls(prev => ({ ...prev, ...batch }))` ran, batch was {}
and the state never picked up any URL — every card rendered the empty
placeholder icon.
Fixed by aliasing first, then replacing pendingUrls with a fresh empty
object (let instead of const on the outer binding). The cloned `batch`
retains its entries for the spread; any new arrivals during the commit
land in the new empty pendingUrls and coalesce into the next frame.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -79,16 +79,18 @@ export function ScreenSourcePicker({ open, onClose, onStart }: Props) {
|
|||||||
// one setState — cuts re-renders from O(N) to O(frames) during the
|
// one setState — cuts re-renders from O(N) to O(frames) during the
|
||||||
// initial fan-in and prevents consecutive 10-30 ms long tasks from
|
// initial fan-in and prevents consecutive 10-30 ms long tasks from
|
||||||
// stacking in one frame.
|
// stacking in one frame.
|
||||||
const pendingUrls: Record<string, string> = {};
|
//
|
||||||
|
// Previously `batch` was aliased to `pendingUrls` and then we cleared
|
||||||
|
// pendingUrls via `delete` — which emptied batch too (same reference)
|
||||||
|
// and every flush ended up spreading nothing into the state. Clone
|
||||||
|
// first, then clear, so the batch keeps its entries.
|
||||||
|
let pendingUrls: Record<string, string> = {};
|
||||||
let rafScheduled = false;
|
let rafScheduled = false;
|
||||||
const flush = () => {
|
const flush = () => {
|
||||||
rafScheduled = false;
|
rafScheduled = false;
|
||||||
if (Object.keys(pendingUrls).length === 0) return;
|
|
||||||
const batch = pendingUrls;
|
const batch = pendingUrls;
|
||||||
// Capture then reset so new arrivals during the commit land in a
|
if (Object.keys(batch).length === 0) return;
|
||||||
// fresh batch instead of double-applying.
|
pendingUrls = {};
|
||||||
const keys = Object.keys(batch);
|
|
||||||
for (const k of keys) delete (pendingUrls as Record<string, string>)[k];
|
|
||||||
startTransition(() => {
|
startTransition(() => {
|
||||||
setThumbnailUrls((prev) => ({ ...prev, ...batch }));
|
setThumbnailUrls((prev) => ({ ...prev, ...batch }));
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user