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

React集成Lightweight Charts时出现'Uncaught TypeError: Cannot read property 'getContext' of null'错误的求助

Fixing "Uncaught TypeError: Cannot read property 'getContext' of null" in Lightweight Charts + React

Let's break down why you're hitting this error and walk through how to fix it. The core problem here is that you aren't cleaning up WebSocket connections and interval timers properly when your component re-renders or unmounts. This leaves stale callbacks that try to interact with chart instances tied to DOM elements that no longer exist—hence the getContext null error.

Why This Happens

Your useEffect hook runs every time fourHourData changes (since it’s in the dependency array). Every time it executes:

  • You create a brand new WebSocket connection
  • You spin up a new interval timer
  • You create a fresh chart instance, then remove the old one in the cleanup function

But the old WebSocket and timer don’t get destroyed—they keep running in the background. When their callbacks fire, they try to use the old candleStickSeries or chart instance that’s already been detached from the DOM. That’s when the error pops up.

Step-by-Step Fixes

1. Clean Up WebSocket Connections

Add code to close the WebSocket in the useEffect cleanup function so old connections don’t linger and fire stale callbacks.

2. Clear Interval Timers

Store the interval ID in a ref, then clear it in the cleanup function to stop the timer from running after the component re-renders or unmounts.

3. Guard Against Null DOM References

Always check if chartRef.current exists before creating the chart—this prevents attempts to initialize a chart on an unmounted element.

4. Fix Prop Destructuring (Small but Critical)

Your child component was receiving props as fourHourData, but you should destructure chartData explicitly to avoid confusion and ensure correct dependency tracking.

Modified Child Component Code

Here’s your FirstChart component with all fixes applied:

function FirstChart({ chartData, setHistoricalData }) {
  const [signals, setSignals] = useState([]);
  const [loading, setLoading] = useState(true);
  const chartRef = React.useRef();
  const intervalRef = React.useRef(null); // Store interval ID for cleanup
  const clientRef = React.useRef(null); // Store WebSocket instance for cleanup

  useEffect(() => {
    // Bail if DOM element doesn't exist yet
    if (!chartRef.current) return;

    console.log("DOM re-rendered.");
    
    // Initialize WebSocket and save to ref
    const client = new W3CWebSocket("wss://stream.binance.com:9443/ws/btcusdt@kline_4h");
    clientRef.current = client;

    const chart = createChart(chartRef.current, {
      watermark: {
        color: "white",
        visible: true,
        fontSize: 18,
        horzAlign: "left",
        vertAlign: "top",
      },
      timeScale: {
        timeVisible: true,
        borderColor: "whitesmoke",
      },
      layout: {
        backgroundColor: "#131722",
        textColor: "whitesmoke",
        fontSize: 12,
        fontFamily: "Arial",
      },
      priceScale: {
        borderColor: "whitesmoke",
      },
      grid: {
        vertLines: {
          style: 0,
          color: "rgba(70, 130, 180, 0.5)",
          visible: true,
        },
        horzLines: {
          style: 0,
          color: "rgba(70, 130, 180, 0.5)",
          visible: true,
        },
      },
    });

    const candleStickSeries = chart.addCandlestickSeries({
      upColor: "#0CCE6B",
      downColor: "#ED254E",
      borderVisible: false,
      wickVisible: true,
      borderColor: "white",
      wickColor: "white",
      borderUpColor: "#84f766",
      borderDownColor: "#ff6939",
      wickUpColor: "#84f766",
      wickDownColor: "#ff6939",
    });

    // Only set data if we have valid chartData
    if (chartData.length > 0) {
      candleStickSeries.setData(chartData);
    }

    client.onmessage = (event) => {
      let message = JSON.parse(event.data);
      // Guard against stale series instance
      if (candleStickSeries) {
        candleStickSeries.update({
          time: message.k.t / 1000,
          open: parseFloat(message.k.o), // Parse Binance's string values to numbers
          high: parseFloat(message.k.h),
          low: parseFloat(message.k.l),
          close: parseFloat(message.k.c),
        });
      }
    };

    const getSignalData = () => {
      const url = "YOUR_SIGNAL_API_URL"; // Replace with your actual API endpoint
      fetch(url, {
        method: "GET",
        mode: "cors",
      })
        .then(checkStatus)
        .then(json)
        .then((response) => {
          const signalData = response.map((data) => {
            return {
              // Your marker data transformation logic here
            };
          });
          // Guard against stale series before updating markers
          if (candleStickSeries) {
            candleStickSeries.setMarkers(signalData);
          }
          setSignals([...signalData.reverse()]);
          setLoading(false);
        })
        .catch((error) => {
          console.log(error);
        });
    };

    getSignalData();
    // Save interval ID to ref for cleanup
    intervalRef.current = setInterval(() => {
      getSignalData();
      console.log("Fetched Long Active Chart again...");
    }, 60 * 1000);

    // Cleanup function: runs on re-render and unmount
    return () => {
      // Close WebSocket connection
      if (clientRef.current) {
        clientRef.current.close();
      }
      // Clear interval timer
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
      }
      // Destroy chart instance
      chart.remove();
    };
  }, [chartData]);

  return (
    <Row className="my-2">
      <Col lg={8} className="d-md-10 d-xl-block mx-auto mb-5 py-5 lightweight-chart" ref={chartRef} />
    </Row>
  );
}

export default FirstChart;

Key Changes Explained

  • Refs for Cleanup: Used refs to track the WebSocket instance and interval ID, so we can access and clean them up in the useEffect cleanup function.
  • Guard Clauses: Added checks to ensure we never call methods on stale or null chart/series instances.
  • Number Parsing: Fixed the WebSocket update to parse Binance's string price values to numbers (Lightweight Charts expects numeric inputs).
  • Prop Destructuring: Corrected the component props to properly destructure chartData for clarity and correct dependency tracking.

Additional Optimization Tip

In your parent component, consider memoizing the chart data arrays with useMemo to avoid unnecessary re-renders of the chart components. For example:

const fourHourDataMemo = useMemo(() => fourHourData, [fourHourData]);

Then pass fourHourDataMemo to FirstChart instead of the raw state—this prevents re-renders if the array reference changes but the data itself doesn’t.

This should eliminate the getContext null error while keeping all your real-time and signal functionality intact.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 05:52:38