跳到正文

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>
}