React中如何对WebSocket关联的频繁重渲染组件进行节流优化
方案优化建议
现有方案的问题
你当前的实现有两个核心隐患:
- 每次WebSocket消息触发
setData时,父组件WebSockets都会全量重渲染,哪怕子组件DataVisualizator被useMemo缓存了,父组件自身的重渲染开销依然存在,高频场景下依然会有性能损耗 - 你手动忽略了
useMemo的依赖data,不符合React Hooks的规范,后续如果逻辑迭代很容易出现闭包陷阱,拿到过期的data值
最优实现思路
不要让高频更新的原始WS数据触发状态更新,把原始数据存在useRef中,单独维护一个给可视化组件的节流状态,只有需要更新可视化的时候才触发状态更新,从根源减少重渲染次数:
- 用
useRef存储每次WS推送的最新完整数据,更新不会触发重渲染 - 定时器间隔到了之后,再把ref里的最新数据同步到给可视化组件的状态里,控制重渲染频率
- 可以搭配
React.memo包裹DataVisualizator,进一步避免不必要的重渲染
优化后的代码实现
1. 无需改动原有的useIntervalHook
2. 改造WebSockets组件
import { useEffect, useRef, useState, memo } from "react"; import useWebSocket from "react-use-websocket"; import { useInterval } from "../Hooks"; // 先把可视化组件用memo包裹,浅比较props决定是否重渲染 const MemoizedDataVisualizator = memo(DataVisualizator); export const WebSockets = () => { const SOCKET_URL = "wss://someWS"; // 用ref存最新的完整数据,更新不会触发重渲染 const latestDataRef = useRef({}); // 专门给可视化组件的节流状态,只有定时器触发时才更新 const [throttledVisualData, setThrottledVisualData] = useState({}); const webSocketOptions = { shouldReconnect: () => true, retryOnError: true, reconnectInterval: 3000, reconnectAttempts: 5, onError: (e) => console.log(e), }; const { sendMessage, lastMessage } = useWebSocket( SOCKET_URL, webSocketOptions ); const handleData = (message: RequestData) => { // 直接更新ref,不会触发重渲染,这里保留你原来的深clone更新逻辑即可 const clonedData = // 原有深拷贝合并新数据的逻辑 latestDataRef.current = clonedData; }; useEffect(() => { lastMessage && handleData(JSON.parse(lastMessage.data)); }, [lastMessage]); // 每700ms同步一次最新数据到可视化状态,触发一次重渲染 useInterval(() => { setThrottledVisualData(latestDataRef.current); }, 700); return ( <> <MemoizedDataVisualizator dataToVisualize={throttledVisualData} /> </> ); };
其他可选方案
如果你的业务确实需要在父组件的state中保留实时的完整数据:
- 还是保留
data状态接收WS实时更新,单独加一个throttledVisualData状态来给可视化组件传值,定时器同步data到throttledVisualData - 父组件的重渲染不可避免的情况下,用
React.memo包裹DataVisualizator,只接收throttledVisualData作为props,就能保证子组件只会在节流间隔更新 - 不建议继续用
useMemo缓存组件实例的写法,不符合React的最佳实践,也容易出现依赖相关的bug
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

