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

React中UseContext仅返回初始值null,WebSocket上下文获取异常

React Context传递WebSocket实例:组件无法获取更新后实例的解决办法

问题背景

通过Context在深层组件访问WebSocket时,组件初始只能拿到null,WebSocket创建完成后组件也不会重新获取实例,加了加载状态后页面一直停留在加载页。控制台打印顺序显示:ControlScreen先输出socket null,之后上下文文件才输出已创建的socket,说明Socket创建时机晚于组件首次渲染,但Context未通知组件更新。

核心问题

当前实现用useRef存储WebSocket实例,但useRef的current属性变更不会触发组件重渲染。Context的value直接绑定ws.current,当ws.current更新时,React检测不到Provider的value变化(因为useRef的引用本身没变),所以子组件不会重新读取Context的新值。

修复方案

改用useState管理WebSocket实例,useState的更新会触发Provider组件重渲染,进而更新Context的value,通知所有消费组件:

1. 修改WebSocket上下文文件

const { createContext, useState, useEffect } = require('react');

const WS_URL = 'ws://localhost:3001';

const WebSocketContext = createContext({ socket: null });

function WebSocketProvider({ children }) {
  const computer = new URLSearchParams(document.location.search).get('computer');
  const [socket, setSocket] = useState(null);

  useEffect(() => {
    const ws = new WebSocket(`${WS_URL}/?computer=${computer}`);
    
    ws.onopen = () => {
      if (ws.readyState === 1) {
        ws.send(JSON.stringify({ computer }));
      }
      setSocket(ws); // 连接成功后更新state,触发Provider重渲染
    };

    ws.onerror = (error) => {
      console.error('WebSocket error:', error);
    };

    return () => {
      ws.close();
    };
  }, [computer]);

  return (
    <WebSocketContext.Provider value={{ socket }}>
      {children}
    </WebSocketContext.Provider>
  );
};

export { WebSocketProvider, WebSocketContext };

2. 优化ControlScreen组件

添加加载状态,并修正消息监听的清理逻辑:

import React, { useContext, useEffect, useState } from "react";
import { WebSocketContext } from "./WebsocketPage";
import { handleControlMessages } from "./HandleMessages";
import CreateScreens from "./CreateScreens";

function ControlScreen() {
  const [consList, setConsList] = useState([]);
  const { socket } = useContext(WebSocketContext);

  useEffect(() => {
    if (!socket) return;

    const handleMessage = (e) => {
      const message = JSON.parse(e.data);
      handleControlMessages(message, setConsList);
    };

    socket.addEventListener('message', handleMessage);
    return () => {
      socket.removeEventListener('message', handleMessage); // 移除指定回调,避免内存泄漏
    };
  }, [socket]);

  // 未获取到socket时显示加载状态
  if (!socket) {
    return <div>Loading...</div>;
  }

  return (
    <div id="controlScreen">
      <h1>Control Screen</h1>
      <CreateScreens consList={consList} />
    </div>
  );
}

export default ControlScreen;

额外优化点

  • 移除了原代码中computer的useRef,因为查询参数是组件初始化时的固定值,直接获取即可,无需用useRef存储。
  • 原清理函数中socket.removeEventListener('message')用法错误,必须传入对应的回调函数才能正确移除监听,否则会导致内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 21:08:21