RequestAnimationFrame-based updates
The browser’srequestAnimationFrame (RAF) API is the foundation for coordinating updates with the display refresh cycle.
RAF callbacks run right before the browser paints. This is the ideal time to apply DOM updates—you get the latest state changes without causing extra frames.
Why RAF matters
Modern React automatically batches updates within event handlers, but using RAF ensures your custom Virtual DOM library coordinates with browser paint timing.
Double buffering for UI consistency
Double buffering maintains two representations of the UI to prevent visual inconsistencies during updates.1
Current tree (screen buffer)
The tree currently displayed to the user. This remains stable during reconciliation.
2
Work-in-progress tree (back buffer)
A clone being reconciled with new props/state. Changes are made here without affecting the visible UI.
3
Swap
Once reconciliation completes, the work-in-progress becomes current in one atomic operation.
Benefits
This pattern is borrowed from graphics programming, where double buffering prevents screen tearing. React’s Fiber architecture uses this extensively.
Priority queues for updates
Not all updates are equally urgent. Priority queues allow treating different updates with different scheduling strategies.Priority levels
Queue management
1
Enqueue update
When state changes, create an update object with priority and enqueue it.
2
Sort by priority
Maintain updates in priority order. Higher priority updates jump the queue.
3
Process in RAF
Inside requestAnimationFrame, process updates starting with highest priority.
4
Yield if needed
If time budget exhausted, save progress and continue next frame.
Priority-based scheduling prevents low-priority background work from delaying critical user interactions. A typing animation should never lag because analytics are processing.
Time slicing for interruptibility
Time slicing breaks long-running reconciliation work into small chunks that can be paused and resumed.The frame budget
By checking
deadline.timeRemaining() before each unit of work, you ensure the browser has time for layout, paint, and input handling—keeping the UI responsive even during heavy reconciliation.Unit of work structure
1
Break into units
Each node in the tree is one unit of work. This ensures consistent, predictable work chunks.
2
Check time before unit
Before processing each node, check if time budget remains.
3
Pause and resume
If budget exhausted, save current position and resume next frame.
4
Complete phase atomically
Once all reconciliation units finish, commit DOM changes in one uninterruptible pass.
Batching strategies
Multiple state updates can often be batched into a single reconciliation pass.Automatic batching
Flush strategies
React 18’s automatic batching extends this to async operations, promises, and timeouts. Your Virtual DOM library should batch aggressively by default but provide escape hatches for synchronous updates.
Putting it together
1
State change occurs
Component calls setState or similar. Create update object with priority.
2
Enqueue and schedule
Add update to priority queue. If no work in progress, schedule RAF callback.
3
Reconciliation starts
RAF fires. Clone current tree to create work-in-progress tree.
4
Time-sliced work
Process units of work (individual nodes) while checking time budget each iteration.
5
Commit or yield
If complete, commit work-in-progress to DOM and swap trees. If time runs out, save progress and continue next frame.
In the Week 6 project, you’ll implement these batching and scheduling strategies in your Virtual DOM library, stress testing with 10,000+ dynamic nodes while profiling layout/paint timings to ensure 60fps performance.
Performance optimization patterns
Read/write batching (FastDOM pattern)
Layout thrashing detection
Use performance marks to detect when your reconciliation work is causing excessive layouts. Each forced synchronous layout is wasted time that could delay your frame.