1- import React , { memo , useEffect , useRef , useState } from "react"
1+ import React , { memo , useEffect , useRef , useState , useMemo } from "react"
22import { useTranslation } from "react-i18next"
3+ import debounce from "debounce"
34
45import MarkdownBlock from "../common/MarkdownBlock"
56import { Clock , Lightbulb } from "lucide-react"
@@ -56,13 +57,45 @@ ElapsedTime.displayName = "ElapsedTime"
5657 * - Heading uses i18n key chat:reasoning.thinking
5758 * - Timer runs while reasoning is active (no persistence)
5859 * - Timer is isolated in a memoized component to prevent full re-renders
60+ * - Content updates are debounced to prevent excessive re-renders during streaming
5961 */
6062export const ReasoningBlock = ( { content, isStreaming, isLast } : ReasoningBlockProps ) => {
6163 const { t } = useTranslation ( )
6264 const startTimeRef = useRef < number > ( Date . now ( ) )
65+ const [ debouncedContent , setDebouncedContent ] = useState < string > ( content || "" )
66+
67+ // Create a debounced function to update content
68+ // This limits content updates to a maximum of ~10 per second (100ms debounce)
69+ const updateDebouncedContent = useMemo (
70+ ( ) =>
71+ debounce ( ( newContent : string ) => {
72+ setDebouncedContent ( newContent )
73+ } , 100 ) ,
74+ [ ] ,
75+ )
76+
77+ // Update debounced content when content changes
78+ useEffect ( ( ) => {
79+ if ( isStreaming ) {
80+ // During streaming, use debounced updates
81+ updateDebouncedContent ( content || "" )
82+ } else {
83+ // When not streaming, update immediately for final content
84+ setDebouncedContent ( content || "" )
85+ // Cancel any pending debounced updates
86+ updateDebouncedContent . clear ( )
87+ }
88+ } , [ content , isStreaming , updateDebouncedContent ] )
89+
90+ // Cleanup debounce on unmount
91+ useEffect ( ( ) => {
92+ return ( ) => {
93+ updateDebouncedContent . clear ( )
94+ }
95+ } , [ updateDebouncedContent ] )
6396
6497 // Only render markdown when there's actual content
65- const hasContent = ( content ?. trim ( ) ?. length ?? 0 ) > 0
98+ const hasContent = ( debouncedContent ?. trim ( ) ?. length ?? 0 ) > 0
6699
67100 return (
68101 < div className = "py-1" >
@@ -75,7 +108,7 @@ export const ReasoningBlock = ({ content, isStreaming, isLast }: ReasoningBlockP
75108 </ div >
76109 { hasContent && (
77110 < div className = "px-3 italic text-vscode-descriptionForeground" >
78- < MarkdownBlock markdown = { content } />
111+ < MarkdownBlock markdown = { debouncedContent } />
79112 </ div >
80113 ) }
81114 </ div >
0 commit comments