P
P
PoisonousHeart2021-03-28 21:28:14
MongoDB
PoisonousHeart, 2021-03-28 21:28:14

What may not be cleared subscriptions in useEffect?

Hello. I ran into a problem using hooks and socket.io. I have no experience and deadlines are burning, so I ask for a competent opinion. Before describing the problem (I know that there are many more problems there and I will only be glad for recommendations on how to eliminate them, since I am getting used to MERN), I want to talk about the situation. So, on the server, the socket is listening for the message " connection" listens to a special message from the client in the same place. If it comes, get the list of users from the database and send it to the client. What the client does is, at the first render, 1 function is created in the callback hook, that is, it will be created 1 time, since with empty deps. Inside the socket connection and setting the incoming data from the server.Now the problem is: on the first display everything is ok, but on re-render some kind of leak occurs.I assume that this is due to the useState hook that I use.In

general, the server code:

io.sockets.on('connection', socket => {
    console.log('socket connected id: ', socket.id)
    socket.emit('connection', 'hello from server')
    listenReqOnUsers(socket, 'getUserList', 'takeUserList') // Слушает запрос на выборку всех пользователей и отсылает
    listenReqOnMessages(socket, 'giveMessageList', 'getMessageList')
  

    socket.on('disconnect', () => {
        console.log('user disconnected: ', socket.id);
      })
    
})


Listener code on the server:
const listenReqOnUsers = (socket, fromChannel, toChannel) => {
    socket.on(fromChannel, async () => {
        const dataUsers = await User.find()
        socket.emit(toChannel, dataUsers)
    })
}

const listenReqOnMessages = (socket, fromChannel, toChannel) => {
    socket.on(fromChannel, async () => {
        const dataUsers = await Message.find()
        socket.emit(toChannel, dataUsers)
    })
}


And the problem client:

import React, { useCallback, useEffect, useState, useRef} from 'react'
import {Link} from 'react-router-dom'
import DialogUser from './DialogUser'

import socket from '../socket'


const UsersList = () => {
    const [usrs, setUsrs] = useState(null) 
    const usrsRef = useRef()

    const [mes, setMes] = useState(false)

    usrsRef.current = usrs
    
    const getUsrs = useCallback(() => {
        socket.emit('getUserList')
        socket.on('takeUserList', dataUsers => {
            console.log('Подключился к прослушиванию сообщений в user list с id: ', socket.id)
            setUsrs(dataUsers)
        })
    }, [])


    useEffect(() => {
        getUsrs()
    }, [])

    useEffect(() => {
        console.log('обновилось: ', usrsRef.current)
    }, [usrsRef.current])
    

    

    return (
        <div>
            {console.log('users: ', usrsRef.current)}
           <div style={{maxHeight: '150px', height:'150px', overflowY: 'scroll'}}>
               <div>
               {usrsRef.current !== null && usrsRef.current !== undefined && usrsRef.current.map((usr, index) => <div key = {index}>
                   {`usr email: ${usr.email}`}
                   <Link to="#" /*onClick = {() => {setMes(!mes)}}*/ className="btn-floating btn-large waves-effect waves-light red"><i className="material-icons">mail</i></Link>
                   {/* <button onClick = {() => setMes(!mes)}/> */}
               </div>)}
               </div>
            </div>
        {/* {mes && <DialogUser/>} */}
        </div>
    )
}

export default UsersList


Screen:

6060cbedbe0bb812980200.png

Answer the question

In order to leave comments, you need to log in

2 answer(s)
V
Vladimir, 2021-03-28
@PoisonousHeart

Read the doc on useEffect
https://reactjs.org/docs/hooks-reference.html#clea
... the component has been unmounted,

console.log('Подключился к прослушиванию сообщений в user list с id: ', socket.id)
            setUsrs(dataUsers)

you 1) move it to a separate function, when mounting, do socket.on(' not on an anonymous function, but on a named one
2) When unmounting, do socket.off if it supports it

P
PoisonousHeart, 2021-03-28
@PoisonousHeart

The answer helped, just in case it is:

useEffect(() => {
        getUsrs()
        return () => {
            socket.offAny('takeUserList')
        }
    }, [])

---------------------------------
I did not take out the named function, I do not understand why I need a context. Or is there some other difference between them? It’s just that I save the function in a variable and in the useState hook, well, it’s clear that I’m calling it, and also, thank you very much, I remove the listener.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question