Хук UseEffect с состоянием socket.io не сохраняется в обработчиках сокетов - PullRequest
0 голосов
/ 22 февраля 2019

У меня есть следующий компонент реагирования

function ConferencingRoom() {
    const [participants, setParticipants] = useState({})
    console.log('Participants -> ', participants)

    useEffect(() => {
        // messages handlers
        socket.on('message', message => {
            console.log('Message received: ' + message.event)
            switch (message.event) {
                case 'newParticipantArrived':
                    receiveVideo(message.userid, message.username)
                    break
                case 'existingParticipants':
                    onExistingParticipants(
                        message.userid,
                        message.existingUsers
                    )
                    break
                case 'receiveVideoAnswer':
                    onReceiveVideoAnswer(message.senderid, message.sdpAnswer)
                    break
                case 'candidate':
                    addIceCandidate(message.userid, message.candidate)
                    break
                default:
                    break
            }
        })
        return () => {}
    }, [participants])

    // Socket Connetction handlers functions

    const onExistingParticipants = (userid, existingUsers) => {
        console.log('onExistingParticipants Called!!!!!')

        //Add local User
        const user = {
            id: userid,
            username: userName,
            published: true,
            rtcPeer: null
        }

        setParticipants(prevParticpants => ({
            ...prevParticpants,
            [user.id]: user
        }))

        existingUsers.forEach(function(element) {
            receiveVideo(element.id, element.name)
        })
    }

    const onReceiveVideoAnswer = (senderid, sdpAnswer) => {
        console.log('participants in Receive answer -> ', participants)
        console.log('***************')

        // participants[senderid].rtcPeer.processAnswer(sdpAnswer)
    }

    const addIceCandidate = (userid, candidate) => {
        console.log('participants in Receive canditate -> ', participants)
        console.log('***************')
        // participants[userid].rtcPeer.addIceCandidate(candidate)
    }

    const receiveVideo = (userid, username) => {
        console.log('Received Video Called!!!!')
        //Add remote User
        const user = {
            id: userid,
            username: username,
            published: false,
            rtcPeer: null
        }

        setParticipants(prevParticpants => ({
            ...prevParticpants,
            [user.id]: user
        }))
    }

    //Callback for setting rtcPeer after creating it in child component
    const setRtcPeerForUser = (userid, rtcPeer) => {
        setParticipants(prevParticpants => ({
            ...prevParticpants,
            [userid]: { ...prevParticpants[userid], rtcPeer: rtcPeer }
        }))
    }

    return (
            <div id="meetingRoom">
                {Object.values(participants).map(participant => (
                    <Participant
                        key={participant.id}
                        participant={participant}
                        roomName={roomName}
                        setRtcPeerForUser={setRtcPeerForUser}
                        sendMessage={sendMessage}
                    />
                ))}
            </div>
    )
}

единственное состояние, которое у него есть - это хеш-таблица участников внутри вызова с использованием ловушки useState для его определения.

затем я использую useEffect для прослушивания событий сокета для чата, всего 4 события

. После этого я определяю 4 обработчика обратного вызова для этих событий с учетом порядка их выполнения на сервере.

и, наконец, у меня есть еще одна функция обратного вызова, которая передается каждому дочернему участнику в списке, так что после того, как дочерний компонент создает свой объект rtcPeer, он отправляет его родителю, чтобы установить его в объекте участника в hashTable участника.

Поток идет так, как участники присоединяются к комнате -> существующий участник событие вызывается -> создается локальный участник и затем добавляется в hashTable участников -> recieveVideoAnswer и кандидат получает сервер несколько раз, как вы можете видеть в screenshot

первое событие состояние пусто, последующие два события его там, потом снова пусто, и этот шаблон повторяет одно пустое состояние, затем следующие два верны, и я понятия не имею, что происходит с состоянием

enter image description here

1 Ответ

0 голосов
/ 22 февраля 2019

Сложность в том, что у вас было несколько проблем, взаимодействующих друг с другом, которые приводили к путанице при устранении неполадок.

Самая большая проблема заключается в том, что вы настраиваете несколько обработчиков событий сокетов.При каждом повторном рендеринге вы звоните socket.on, даже не позвонив socket.off.

Вам необходимо использовать один из двух подходов:

  • Установить обработчик событий одного сокета.При таком подходе вы использовали бы пустой массив зависимостей для useEffect, но это означает, что вы не можете ссылаться на participants в любом месте внутри вашего эффекта (включая все методы, вызываемые вашим обработчиком сообщений),Если вы сделаете ссылку participants, вы будете ссылаться на старую версию, как только произойдет первый повторный рендеринг.

  • Установите новый обработчик событий сокета с каждым изменением на participants.Для правильной работы необходимо удалить предыдущий обработчик событий , в противном случае у вас будет такое же количество обработчиков событий, что и для визуализации.Когда у вас есть несколько обработчиков событий, первый созданный будет всегда использовать первую версию participants (пусто), второй всегда будет использовать вторую версию participants и т. Д.

В любом случае, я думаю, вам будет легче рассуждать о том, что делает код, если вы переместите свой обработчик сообщений из функции рендеринга и явно передадите его зависимости.

Второйопция более гибкая для работы, поскольку позволяет работать с participants вне функционального обновления setParticipants, поэтому я просто покажу эту опцию.С этой опцией нет необходимости использовать синтаксис функционального обновления с setParticipants, но вам нужно всегда удалять предыдущий обработчик событий, используя метод очистки, возвращаемый из useEffect.

Вот чтокод будет выглядеть как со вторым вариантом (я не пытался выполнить это, поэтому я не даю никаких гарантий, что у меня нет незначительных синтаксических проблем):

const messageHandler = (message, participants, setParticipants) => {
    console.log('Message received: ' + message.event);

    const onExistingParticipants = (userid, existingUsers) => {
        console.log('onExistingParticipants Called!!!!!');

        //Add local User
        const user = {
            id: userid,
            username: userName,
            published: true,
            rtcPeer: null
        };

        setParticipants({
            ...participants,
            [user.id]: user
        });

        existingUsers.forEach(function (element) {
            receiveVideo(element.id, element.name)
        })
    };

    const onReceiveVideoAnswer = (senderid, sdpAnswer) => {
        console.log('participants in Receive answer -> ', participants);
        console.log('***************')

        // participants[senderid].rtcPeer.processAnswer(sdpAnswer)
    };

    const addIceCandidate = (userid, candidate) => {
        console.log('participants in Receive canditate -> ', participants);
        console.log('***************');
        // participants[userid].rtcPeer.addIceCandidate(candidate)
    };

    const receiveVideo = (userid, username) => {
        console.log('Received Video Called!!!!');
        //Add remote User
        const user = {
            id: userid,
            username: username,
            published: false,
            rtcPeer: null
        };

        setParticipants({
            ...participants,
            [user.id]: user
        });
    };

    //Callback for setting rtcPeer after creating it in child component
    const setRtcPeerForUser = (userid, rtcPeer) => {
        setParticipants({
            ...participants,
            [userid]: {...participants[userid], rtcPeer: rtcPeer}
        });
    };

    switch (message.event) {
        case 'newParticipantArrived':
            receiveVideo(message.userid, message.username);
            break;
        case 'existingParticipants':
            onExistingParticipants(
                    message.userid,
                    message.existingUsers
            );
            break;
        case 'receiveVideoAnswer':
            onReceiveVideoAnswer(message.senderid, message.sdpAnswer);
            break;
        case 'candidate':
            addIceCandidate(message.userid, message.candidate);
            break;
        default:
            break;
    }
};

function ConferencingRoom() {
    const [participants, setParticipants] = useState({});
    console.log('Participants -> ', participants);

    useEffect(() => {
        const handler = (message) => {messageHandler(message, participants, setParticipants)};
        socket.on('message', handler);
        return () => {
            // THIS IS THE IMPORTANT CHANGE
            socket.off('message', handler);
        }
    }, [participants]);

    return (
            <div id="meetingRoom">
                {Object.values(participants).map(participant => (
                        <Participant
                                key={participant.id}
                                participant={participant}
                                roomName={roomName}
                                setRtcPeerForUser={setRtcPeerForUser}
                                sendMessage={sendMessage}
                        />
                ))}
            </div>
    );
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...