stevenH
stevenH

Reputation: 175

Is there a way to avoid re-render continuously when calling a WebSocket from React component?

I have a stuck with avoiding component re-render when I use a Websocket for receiving data and update them to UI. I have a component named A and I use a Websocket into it for receiving a real-time message and then show it to UI. I use "useEffect" hook like that:

function User() {
  let ws = new Websocket(host);
  let [users, setUser] = useState([]);

  useEffect(() => {
   let message = [];
   ws.onmessage = evt => {
     message = JSON.parse(evt.data);
     setUser(message);
   }}, [users]);
 
 return (
    <div className="user-table">
      <Table users={users} />
    </div>
   )
}

Because the Websocket frequently has the new data (1 second), it causes the component always update. So, is there any way for avoiding this problem? (caching or something like that)

Upvotes: 1

Views: 4930

Answers (1)

CertainPerformance
CertainPerformance

Reputation: 370789

The problem here is not the socket in itself, but the fact that your useEffect dependency array contains a state, and that the useEffect callback adds a listener, and that the callback changes the state. Every time setUser is called, users changes - and when users changes, the useEffect runs again, because one of its dependencies changed.

Remove users from the dependency array, and only declare the socket once, in the useEffect so that you only have one socket (per User, at least...) active at a time.

function User() {

  const [users, setUsers] = useState([]);

  useEffect(() => {
    const ws = new Websocket(host);
    ws.onmessage = evt => {
      setUsers(JSON.parse(evt.data));
    };
    // Close socket on unmount:
    return () => ws.close();
  }, []); // <-- empty dependency array; only run callback once, on mount
 
  return (
    <div className="user-table">
      <Table users={users} />
    </div>
   );
}

Also note:

  • Best to prefer const over let in ES6
  • To reduce bugs, name variables appropriately - if you have multiple users, use users and setUsers. (If you have only a single user, use user and setUser)

If there's any chance of there being more than one User, it would be better to create the socket just once in the parent, then pass it down as a prop; that way, you won't be creating multiple sockets which all do the same thing.

Upvotes: 2

Related Questions