Skip to content

Conversation

@AVGVSTVS96
Copy link
Owner

@AVGVSTVS96 AVGVSTVS96 commented Dec 5, 2025

This PR replaces the imperative throttleHighlighting function with a reactive useThrottledDebounce hook.

Acknowledgment

Implementation inspired by streamdown's useThrottledDebounce hook, originally authored by @haydenbleasel.

Changes

New hybrid throttle+debounce behavior:

  • Immediate update when throttle window has passed
  • Debounce for rapid changes within the throttle window
  • Proper cleanup to prevent memory leaks

Code simplification:

  • Removes TimeoutState interface
  • Moves throttling to a dedicated hook instead of inline effect logic
  • Value-based pattern instead of imperative callback

API

No breaking changes - public API remains identical:

  • delay option still controls throttling
  • Default behavior unchanged (no throttling when delay is omitted/undefined)

Test plan

  • Existing tests pass
  • New unit tests for useThrottledDebounce hook
  • Integration test verifies delay option works end-to-end

Port hybrid throttle+debounce pattern from streamdown:
- Immediate update when throttle window has passed
- Debounce for rapid changes within throttle window
- Value-based hook instead of imperative callback pattern
- Remove TimeoutState type (no longer needed)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@changeset-bot
Copy link

changeset-bot bot commented Dec 5, 2025

🦋 Changeset detected

Latest commit: 386b9dc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
react-shiki Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Dec 5, 2025

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

Project Deployment Preview Comments Updated (UTC)
react-shiki Ready Ready Preview Comment Dec 12, 2025 1:41pm

@claude
Copy link

claude bot commented Dec 5, 2025

Claude finished @AVGVSTVS96's task —— View job


Claude finished @AVGVSTVS96's task —— View job


Code Review: useThrottledDebounce Hook

I've completed a comprehensive review of the throttle-debounce performance improvements. Here's my assessment:

Overall Assessment: Excellent Implementation

This PR successfully modernizes throttling from imperative to reactive patterns with excellent architecture and thorough testing.


🎯 Strengths

Clean Architecture & Design

  • Perfect separation of concerns: Moving throttling logic to a dedicated hook
  • Hybrid throttle+debounce strategy: Immediate updates when throttle window has passed, debouncing within the window - this is superior to pure throttling
  • Proper cleanup: useEffect cleanup function prevents memory leaks from dangling timeouts

API Compatibility

  • Zero breaking changes: Public API remains identical (delay option still works as expected)
  • Backward compatible: No throttling when delay is undefined maintains existing behavior
  • Type safety maintained: Proper TypeScript support throughout

Implementation Quality

  • Memory leak prevention: timeoutRef cleanup in useEffect return (utils.ts:64-68)
  • Race condition handling: clearTimeout before setting new timeout (utils.ts:50-52)
  • Edge case handling: Proper handling of undefined throttleMs (utils.ts:42-45)
  • Performance optimized: Uses Date.now() for time calculations instead of more expensive alternatives

Code Simplification

  • Removed complexity: Eliminates TimeoutState interface and imperative timeout management
  • Cleaner hook: Main hook now uses simple value-based pattern with useThrottledDebounce(code, stableOpts.delay) (hook.ts:54)
  • Better separation: Throttling logic isolated from main highlighting logic

Excellent Test Coverage

  • Comprehensive testing: Tests cover throttling behavior, immediate updates, debouncing, and edge cases
  • Integration tests: End-to-end delay option testing (hook.test.tsx:922-944)
  • Unit tests: Isolated useThrottledDebounce testing (hook.test.tsx:899-920)

🔍 Technical Deep Dive

New useThrottledDebounce Hook (utils.ts:30-72)

// Excellent hybrid approach:
if (timeSinceLastRun >= throttleMs) {
  setProcessedValue(value);           // Immediate update
  lastRunTime.current = now;
} else {
  timeoutRef.current = setTimeout(() => {
    setProcessedValue(value);         // Debounced update
    lastRunTime.current = Date.now();
  }, debounceMs);
}

Why this is superior:

  • Responsive: Immediate updates when not rate-limited
  • Smooth: Debouncing prevents rapid-fire updates during typing
  • Efficient: Avoids unnecessary processing during rapid changes

Changeset Quality

  • Proper versioning: Correctly marked as patch since it's internal refactoring
  • Clear description: Matches the actual implementation changes

💡 Minor Suggestions

1. Documentation Enhancement

Consider adding JSDoc example to useThrottledDebounce:

/**
 * @example
 * // Throttle search queries with 300ms throttle + 50ms debounce
 * const throttledQuery = useThrottledDebounce(searchQuery, 300);
 */

2. Consider Making debounceMs Configurable

Currently hardcoded at 50ms. Could be useful as an option:

useThrottledDebounce(value, throttleMs, debounceMs = 50)

3. Potential Race Condition Edge Case

In useThrottledDebounce, there's a tiny window where lastRunTime.current is set to Date.now() in the timeout callback (line 60) vs now in the immediate path (line 56). Consider using consistent timing:

const updateTime = Date.now();
lastRunTime.current = updateTime;

🚀 Performance & Security

  • Performance: Improved - less object allocation, simpler state management
  • Memory: Better - proper cleanup prevents leaks
  • Security: No concerns - no user input validation issues
  • Bundle size: Reduced - eliminated TimeoutState interface

Requirements Check

  • Changeset included: Proper patch-level changeset
  • Clean architecture: Excellent separation of concerns
  • Test coverage: Comprehensive testing of all scenarios
  • No breaking changes: API compatibility maintained
  • Documentation: Internal docs are clear (public docs unchanged as expected)

🏆 Recommendation: APPROVE

This is an exemplary refactoring that improves code quality, maintainability, and user experience while maintaining perfect backward compatibility. The hybrid throttle+debounce approach is particularly well-designed for the syntax highlighting use case.

Special acknowledgment: The attribution to @haydenbleasel and streamdown demonstrates good open source practices.

Copy link
Owner Author

AVGVSTVS96 commented Dec 5, 2025

@AVGVSTVS96 AVGVSTVS96 changed the title refactor: replace throttleHighlighting with useThrottledDebounce hook refactor: replace throttling with useThrottledDebounce hook Dec 5, 2025
@AVGVSTVS96 AVGVSTVS96 marked this pull request as ready for review December 5, 2025 10:40
@AVGVSTVS96 AVGVSTVS96 changed the title refactor: replace throttling with useThrottledDebounce hook perf: replace throttling with useThrottledDebounce hook Dec 5, 2025
@AVGVSTVS96 AVGVSTVS96 changed the title perf: replace throttling with useThrottledDebounce hook perf: improve throttling with useThrottledDebounce hook Dec 5, 2025
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