fix(streamdown): use useDeferredValue for blocks-state, prevent React #185 cascade#530
Open
cristicretu wants to merge 1 commit into
Open
Conversation
…ercel#185 cascade The internal pattern of mirroring `blocks` into `displayBlocks` via useState + useEffect-to-sync + manual startTransition fires setDisplayBlocks(blocks) on every render where `blocks` is a new reference. Under SSE bursts that deliver tokens faster than React commits, those setStates stack inside one commit cycle and exceed React's 50-nested-update limit, triggering Error: Maximum update depth exceeded (React vercel#185). Replace with useDeferredValue, which performs the same role (low-priority blocks-state update during streaming) declaratively and without producing setStates that can cascade. - SSR/hydration unchanged (deferred value === current value on first render). - animatePlugin path kept synchronous (non-deferred) because the animation plugin reads block content per-render and must see the freshest value. - 982/982 existing tests pass. Refs: vercel#140 (closed with experimental_throttle workaround; this fixes the root cause).
Contributor
|
@cristicretu is attempting to deploy a commit to the Vercel Team on Vercel. A member of the Team first needs to authorize it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
We hit React #185 with Streamdown in production at Anara. About 15 events/hr at peak across thousands of users. Bumping
experimental_throttleto 100ms (per #140) closed roughly 86% of those, but the rest needed a code change.The cascade sits in
index.tsx:Each streaming token gives
blocksa new ref, the effect fires,setDisplayBlocksqueues an update.startTransitionlowers the priority but doesn't drop intermediates, so under SSE bursts you stack 50+ updates inside one commit and trip React's nested-update guard.useDeferredValuedoes the same job without the setStates:First render returns
blocksdirectly so SSR hydration is unchanged. TheanimatePluginpath stays synchronous because the plugin needs the freshest content per render.