React中SSE更新状态未触发重渲染的原因及解决方案
问题根因
React 对引用类型(数组、对象)的状态变更判断依赖引用相等性:只有当状态的内存引用地址发生变化时,React 才会判定状态更新,进而触发重渲染。
你的代码中,setPromptsResultsArray 的更新函数直接修改了原状态数组 prevPromptsResultsArray 的内部项,最终返回的还是原数组本身,数组的引用地址完全没有变化,React 检测不到状态变更,因此不会触发重渲染。只有当 prompt 状态变化、组件因其他原因重渲染时,之前直接修改的数组值才会被渲染到页面上,这就是你观察到“B的更新要等A再次变化才显示”的核心原因。
规范修复方案
React 状态更新的核心规则是:永远不要直接修改原状态,所有针对引用类型状态的更新,必须返回一个全新的引用副本。
- 修复SSE消息回调中的状态更新逻辑,替换为返回新数组副本的写法:
newBackendEventSource.addEventListener('message', (e) => { const data = JSON.parse(e.data); setPromptsResultsArray((prevPromptsResultsArray) => { const index = prevPromptsResultsArray.length - data.counter - 1 // 创建原数组的浅拷贝,不直接修改原数组 const nextArray = [...prevPromptsResultsArray] // 替换对应位置的项,嵌套对象也要创建新副本,不直接修改原对象 nextArray[index] = { ...nextArray[index], isLoading: false, URIs: [data.uri] } // 返回全新引用的数组,React可检测到变更触发渲染 return nextArray }); });
- 补充useEffect清理逻辑,避免内存泄漏:
你当前的useEffect只在prompt变化时创建新的SSE连接,但没有关闭旧连接的逻辑,会导致重复创建连接、重复接收消息。需要在useEffect中返回清理函数,在依赖变化、组件卸载时关闭旧连接:
useEffect(() => { const newBackendEventSource = new EventSource(url, {withCredentials: false}) // 原有事件监听逻辑保持不变 newBackendEventSource.addEventListener('open', () => { console.log('SSE opened!'); }); // ... 其余open/error/close/message监听逻辑不变 // 新增清理逻辑 return () => { newBackendEventSource.close() } }, [prompt]);
- 可选优化:将状态初始值移到组件外,或传入useState的初始化函数,避免组件每次渲染都重复创建初始数组对象,减少不必要的性能开销。
内容的提问来源于stack exchange,提问作者Rylan Schaeffer
相关产品推荐
相关产品推荐

