首页 > 解决方案 > 将 websocket ref 传递给子组件

问题描述

我已经在父组件中建立了一个 websocket 连接,并socket使用useRef. 现在,我将它传递socket给道具中的子组件。我想在子组件中使用此套接字来发出组件特定事件并根据某些 websocket 事件更新特定于组件的某些状态变量。

父组件

function Parent(props) {
  const userId = props.userId;
  const socket = useRef(null);

  useEffect(() => {
    if (userId) {
      socket.current = services.establishSocketConnect(userId);
    }
    return () => {
      socket.current.close();
    };
  }, []);

  return (
    <React.Fragment>
      <Child socket={socket} />
    </React.Fragment>
  );
}

const mapStateToProps = (state) => ({
  userId:
    (state.userInfo && state.userInfo.user && state.userInfo.user._id) || null,
});

export default connect(mapStateToProps, null)(Parent);

子组件

function Child(props) {
  const { socket } = props;

  useEffect(() => {
    console.log('Child => socket', socket);
    if (socket.current) {
      console.log('attaching socket events');
    }
  }, []);

  return <h1>Child</h1>;
}

const mapStateToProps = (state, otherProps) => {
  return {
    // ...
    ...otherProps,
  };
};

export default connect(mapStateToProps, null)(Child);

在子组件中,socket.current没有 websocket 连接。我知道在初始渲染时,该值将为空。所以,这就是 Child 组件最初收到的内容。因此,我尝试添加socket,socket.current以便useEffectChild 组件在更改时重新渲染。但是,这并没有发生,因为可变对象不能用作依赖项useEffect,我猜..

请提出解决此问题的方法。


使用Robin Zigmond 的解决方案和一些额外的日志记录。

家长

function Parent(props) {
  const userId = props.userId;
  const socket = useRef(null);

  useEffect(() => {
    console.log('Parent => (initial) socket', socket);

    socket.current = services.establishSocketConnection(userId);

    if (socket.current) {
      socket.current.onopen = onWSOpenEvent();
    }

    let i = setInterval(() => console.log('Parent => (interval) socket', socket), 5000);

    return () => {
      clearInterval(i);
    };
  }, [userId]);

  return (
    <React.Fragment>
      <Child socket={socket.current} />
    </React.Fragment>
  );
}

孩子

function Child(props) {
  const { socket } = props;

  useEffect(() => {
    console.log('Child => (initial) socket', socket);
    if (socket) {
      console.log('attaching socket events');
    }

    let i = setInterval(() => console.log('Child => (interval) socket', socket), 5000);

    return () => {
      clearInterval(i);
    };
  }, [socket]);

  return <h1>Child</h1>;
}

这没有用。以下是日志:

日志

标签: javascriptreactjssocketswebsocket

解决方案


问题是您将socket作为道具传递给子组件 - 按照设计,它是一个可变对象,其current属性会发生变化,同时保持整个对象的相同引用。但是当你将整个对象传递给子组件时,这与 React 道具不能很好地配合——因为当该current属性发生变化时,React 不会“看到”这种变化(道具仍然和以前一样——a引用同一个对象),所以它不会重新渲染孩子。

解决方案非常简单 - 不要将可变对象作为道具传递,而只是传递current您关心的属性:

<Child socket={socket.current} />

现在,每当您socket在 Child 组件中引用 prop 时,它都应该具有实际的 websocket 连接。

编辑:如果您希望在子组件中记录当前套接字值,每次更改时,子组件useEffect 中的

useEffect(() => {
    console.log('Child => socket', socket);
    if (socket) {
      console.log('attaching socket events');
    }
}, [socket]);

推荐阅读