简体   繁体   中英

Passing websocket ref to child component

I have established a websocket connection in a Parent component and stored that in a variable socket using useRef . Now, I am passing this socket to a Child component in the props. I want to use this socket in the child component to emit component specific events and update certain state variables specific to the component based on some websocket events.

Parent Component :

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);

Child Component :

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);

In the Child Component, socket.current is not having the websocket connection. I understand that at the initial render, the value will be null. So, that's what the Child component is receiving initially. So, I tried to add socket , socket.current to useEffect so that Child component would re-render on change of this. But, this is not happening because mutable objects cannot be used as dependencies for useEffect , I guess..

Please suggest a way to solve this.


Using Robin Zigmond's solution with some additional logging.

Parent

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>
  );
}

Child

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>;
}

This did not work. Here are the logs:

日志

The problem is that you are passing socket as a prop to the child component - which, by design, is a mutable object, whose current property gets changed while maintaining the same reference for the entire object. But that doesn't play nicely with React props when you pass the whole object down to the child component - because when that current property changes, React doesn't "see" that change (the prop is still the same as it was before - a reference to the same object) so it won't rerender the child.

The solution is very simple - don't pass the mutable object as a prop, but just the current property that you care about:

<Child socket={socket.current} />

Now whenever you reference the socket prop in the Child component, it should have the actual websocket connection.

Edit: if you want the current socket value logged in the child component, every time it changes, then the useEffect in the child should be

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

Here is what i do when passing websocket ref from parent to child component.

Parent Component:

 import React, {useRef, useEffect} from "react"; import { io } from "socket.io-client"; const Parent = () => { const socketClientRef = useRef(); useEffect(() => { const client = io(SOCKET_URL); client.on("connect", () => { console.log("Connected"); }); client.on("disconnected", () => { console.log("Disconnected"); }); return () => { client.removeAllListeners(); }; }, []); return ( <> <ChildComponent client={socketClientRef.current} /> </> ); } export default Parent;

Child Component:

 import React, { useEffect } from "react"; const Child = ({client}) => { useEffect(() => { if (typeof client.== "undefined") { if (client.connected === true) { client,on("connect". () => { console;log("Connect From Child Component"); }), } } }; [client]); }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM