Skip to content

fix: useStream can now handle multiple streams per component lifetime#20

Draft
ianmacartney wants to merge 1 commit intomainfrom
cursor/text-streaming-persistence-on-disconnect-4721
Draft

fix: useStream can now handle multiple streams per component lifetime#20
ianmacartney wants to merge 1 commit intomainfrom
cursor/text-streaming-persistence-on-disconnect-4721

Conversation

@ianmacartney
Copy link
Copy Markdown
Member

Summary

Fixes #19

The useStream hook was unable to drive more than one stream per component lifetime. After the first stream completed, subsequent calls with a new streamId silently skipped the HTTP POST and fell back to the database query, losing the real-time streaming UX.

Root Causes Addressed

1. streamStarted ref was set but never cleared

The original code used a streamStarted ref intended as a React Strict Mode guard, but it permanently blocked all future streams after the first one completed.

Fix: Replaced with activeStreamRef that tracks the current streamId. This serves as both:

  • A Strict Mode guard (same streamId won't re-trigger)
  • A mechanism to detect when a new stream should be started

2. streamBody and streamEnded were never reset between streams

When streamId changed, the state from the previous stream would persist, potentially causing text concatenation issues.

Fix: Both states are now reset when a new streamId is detected.

3. No AbortController caused stale stream reads to leak

If the component re-rendered with a new streamId while a previous fetch was mid-read, the old reader.read() loop would continue calling setStreamBody, mixing text across streams.

Fix: Added AbortController to cleanup in-flight requests when:

  • Component unmounts
  • streamId changes to a new value

Errors from aborted fetches are silently ignored to avoid false error states.

4. Minor performance improvement

TextDecoder was being allocated per chunk inside the while loop.

Fix: Now allocated once per stream and reused.

Testing

  • TypeScript type checking passes (npm run typecheck)
  • ESLint passes (npm run lint)
  • Tests pass (npm test)

Slack Thread

Open in Web Open in Cursor 

Fixes #19

Root causes addressed:
1. streamStarted ref was set but never cleared, blocking all future streams
   - Replaced with activeStreamRef that tracks the current streamId
   - Acts as both a Strict Mode guard AND allows detecting new streams

2. streamBody and streamEnded were never reset between streams
   - Now reset both states when a new streamId is detected

3. No AbortController caused stale stream reads to leak
   - Added AbortController to cleanup in-flight requests when:
     - Component unmounts
     - streamId changes to a new value
   - Errors from aborted fetches are now silently ignored

4. TextDecoder was allocated per chunk (minor perf issue)
   - Now allocated once per stream and reused

The fix enables the real-time streaming UX for multiple consecutive
messages in the same component session, rather than falling back to
database persistence after the first stream completes.

Co-authored-by: Ian Macartney <ianmacartney@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 25, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 67d28cce-77e7-44b6-889a-87ca12aed9a0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/text-streaming-persistence-on-disconnect-4721

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 25, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@convex-dev/persistent-text-streaming@20

commit: cbb178d

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.

useStream: streamStarted ref never resets — HTTP POST only fires once per mount

2 participants