React应用中WebSocket发送JSON消息引发重复渲染问题求助
解决WebSocket提交后组件无限重渲染的问题
核心问题分析
你的代码存在两个关键问题,共同导致了循环重渲染:
useWebSocket的配置对象每次组件渲染都会生成新的引用,触发hook重新初始化WebSocket连接;连接建立后收到消息更新lastJsonMessage,又会引发组件重渲染,最终形成循环。onSubmitClick的useCallback依赖项缺失sendJsonMessage,导致函数引用不稳定,既不符合React Hooks的依赖规则,也可能触发子组件不必要的重渲染。
具体修复步骤
1. 缓存WebSocket配置对象
用useMemo包裹useWebSocket的配置参数,确保每次渲染时配置对象的引用一致,避免hook重复初始化:
const wsOptions = useMemo(() => ({ shouldReconnect: (closeEvent) => true, reconnectInterval: 100, }), []); const { sendJsonMessage, lastJsonMessage, readyState, getWebSocket } = useWebSocket("ws://localhost:8425/", wsOptions);
2. 补全useCallback的依赖项
将sendJsonMessage加入useCallback的依赖数组,保证函数引用稳定:
const onSubmitClick = useCallback(() => { sendJsonMessage({ subscribe: "DE000BASF111" }); }, [sendJsonMessage])
3. 正确处理WebSocket返回的消息
如果需要用lastJsonMessage更新ISINData,必须通过useEffect监听消息变化,避免在渲染阶段直接更新状态引发循环:
useEffect(() => { if (lastJsonMessage) { // 根据实际消息格式调整更新逻辑 setISINData(prev => [...prev, lastJsonMessage]); } }, [lastJsonMessage]);
修复后的完整代码片段
const [ISINData, setISINData] = useState<ISINDataType[]>([]); const wsOptions = useMemo(() => ({ shouldReconnect: (closeEvent) => true, reconnectInterval: 100, }), []); const { sendJsonMessage, lastJsonMessage, readyState, getWebSocket } = useWebSocket("ws://localhost:8425/", wsOptions); const onSubmitClick = useCallback(() => { sendJsonMessage({ subscribe: "DE000BASF111" }); }, [sendJsonMessage]) // 处理WebSocket返回的消息 useEffect(() => { if (lastJsonMessage) { // 按实际业务逻辑更新ISINData setISINData(prevData => [...prevData, lastJsonMessage]); } }, [lastJsonMessage]); return ( <div className="App h-full container"> <span>The WebSocket is currently {connectionStatus}</span> <div className="flex w-full h-full flex-col justify-center"> <div className="flex items-center p-4 box-border-sizing justify-center"> <ISINForm onSubmit={onSubmitClick} className="flex items-center" /> </div> <Card classNames={classNameCreator([ "flex-grow-1 pr-4 pl-4 box-border-sizing", styles.content, ])} > <ul className={classNameCreator([styles["watch-list"], "p-0 m-0"])}> {ISINData?.map((ISIN) => ( <ISINCard ISINData={ISIN} key={ISIN?.isin} onClick={() => onRowClick(ISIN?.isin)} /> ))} </ul> </Card> </div> </div> );
额外优化建议
- 给
ISINForm组件加上React.memo包裹,避免因父组件传递的onSubmit函数引用变化而触发不必要的重渲染。 - 如果
connectionStatus是基于readyState计算的,也用useMemo缓存计算结果,进一步稳定渲染逻辑。
内容的提问来源于stack exchange,提问作者N.SH
相关产品推荐
相关产品推荐

