internal demo
LLM 流式输出渲染:Ref + setInterval
token 进入 useRef 缓冲区, 定时器每 50ms 把缓冲同步到 state。React 对字符串做值比较,内容没变就 bail out, 渲染次数应该远小于 token 数量。
run #0 · 119 字符
渲染次数
0
文本长度:0 / 119
核心代码▾
'use client'
import { useEffect, useMemo, useRef, useState } from 'react'
import { highlight } from 'sugar-high'
const SAMPLE_TEXT = '...'
export default function StreamDemo() {
const [text, setText] = useState('')
const bufferRef = useRef('')
const lastFlushedLenRef = useRef(0)
useEffect(() => {
const flushTimer = setInterval(() => {
// 只在 buffer 真的长了的时候同步,数字比较,比 setText 里的字符串 diff 便宜
if (bufferRef.current.length !== lastFlushedLenRef.current) {
lastFlushedLenRef.current = bufferRef.current.length
setText(bufferRef.current) // React 对字符串做值比较,内容相同就 bail out
}
}, 50)
const ws = new WebSocket('/api/chat')
ws.onmessage = (e) => {
// token 进 ref,完全不走 React 渲染
bufferRef.current += e.data
}
return () => {
clearInterval(flushTimer)
ws.close()
}
}, [])
return <div className="stream">{text || ' '}</div>
}