You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中通过WebSocket获取Bitstamp实时数据?解决循环卡顿与渲染异常

解决Bitstamp WebSocket实时数据的无限循环与渲染问题

我来帮你搞定这两个头疼的问题——页面无限循环消耗资源,还有状态更新后页面不渲染的问题,咱们一步步拆解修复:

一、为什么会无限循环?

你的代码里有两个核心问题导致了恶性循环:

  1. WebSocket实例重复创建:组件顶层的const ws = new WebSocket('wss://ws.bitstamp.net');会在每次组件重新渲染时都新建一个WebSocket连接,加上useEffect依赖了orders,每次setOrders更新状态就会触发useEffect重新执行,进而再次调用initWebsocket开启新连接,资源被疯狂消耗。
  2. 不必要的useEffect依赖:orders是我们要更新的状态,根本不需要放到useEffect的依赖数组里——我们是通过WebSocket回调来更新它,而不是依赖它去触发WebSocket操作。

二、为什么页面不重新渲染?

  1. showResult函数没有返回JSX:你写了map逻辑,但没把生成的<tr>元素return出去,所以即使orders更新了,页面也不会有任何变化。
  2. 状态类型不匹配:初始orders是数组[],但Bitstamp返回的订单簿是包含bids和asks的对象,第一次渲染时orders.bids会是undefined,可能导致报错或渲染失败。

完整修复代码

import { useState, useEffect, useRef } from 'react';

const OrderBook = () => {
  const [loading, setLoading] = useState(true);
  // 初始化orders为符合接口格式的空对象,避免类型不匹配
  const [orders, setOrders] = useState({ bids: [], asks: [] });
  const subscription = { event: 'bts:subscribe', data: { channel: 'order_book_btcusd' } };
  // 用useRef保存WebSocket实例,避免每次渲染都新建
  const wsRef = useRef(null);

  const initWebsocket = () => {
    // 先关闭之前的连接(如果存在)
    if (wsRef.current) {
      wsRef.current.close();
    }
    // 创建新的WebSocket连接并保存到ref
    wsRef.current = new WebSocket('wss://ws.bitstamp.net');

    wsRef.current.onopen = () => {
      console.log('WebSocket连接成功');
      wsRef.current.send(JSON.stringify(subscription));
    };

    wsRef.current.onmessage = (event) => {
      const response = JSON.parse(event.data);
      switch (response.event) {
        case 'data':
          setOrders(response.data);
          setLoading(false);
          break;
        case 'bts:request_reconnect':
          console.log('请求重新连接');
          initWebsocket();
          break;
        default:
          break;
      }
    };

    wsRef.current.onclose = () => {
      console.log('WebSocket连接关闭,尝试重连');
      // 加个延迟避免频繁重连
      setTimeout(() => initWebsocket(), 3000);
    };

    wsRef.current.onerror = (error) => {
      console.error('WebSocket错误:', error);
    };
  };

  useEffect(() => {
    // 组件挂载时初始化一次WebSocket
    initWebsocket();

    // 组件卸载时关闭连接,清理资源
    return () => {
      if (wsRef.current) {
        wsRef.current.close();
      }
    };
    // 依赖数组为空,只在组件挂载和卸载时执行
  }, []);

  // 修复showResult,返回JSX,同时做空值判断
  const showResult = () => {
    if (loading || !orders.bids.length) {
      return <tr><td colSpan="2">加载中...</td></tr>;
    }
    return orders.bids.map((el, index) => (
      <tr key={index}>
        <td>{el[0]}</td>
        <td>{el[1]}</td>
      </tr>
    ));
  };

  return (
    <div>
      <h2>BTC/USD 订单簿</h2>
      <table border="1">
        <thead>
          <tr>
            <th>价格</th>
            <th>数量</th>
          </tr>
        </thead>
        <tbody>
          {showResult()}
        </tbody>
      </table>
    </div>
  );
};

export default OrderBook;

关键改动说明

  • 用useRef保存WebSocket实例:确保整个组件生命周期内只有一个WebSocket连接,避免重复创建。
  • 调整useEffect依赖:依赖数组为空,只在组件挂载时初始化一次,组件卸载时关闭连接,防止内存泄漏。
  • 修复showResult的返回值:明确return生成的JSX,同时添加加载状态和空值判断,避免报错。
  • 初始化orders为正确类型:避免初始数组和后续对象的类型不匹配问题。
  • 添加重连延迟:WebSocket断开后延迟3秒再重连,避免频繁请求导致资源浪费。

内容的提问来源于stack exchange,提问作者SAMA BALA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:05:48