Я использую React
и Socket.io
, чтобы создать какой-то клон Slack. В моем случае я пытаюсь объединить namespaces
и rooms
.
Я присоединяюсь к namespace
, передавая параметры маршрута в моем маршруте React Router
.
У меня есть следующие маршруты:
<Switch>
<Route path={'/server/:id'} component={ServerContentPage} />
</Switch>
и внутри ServerContentPage У меня есть:
<Route exact path={'/server/:id/room/:roomID'} component={ChatPage} />
, поэтому маршрут ChatPage вложен в ServerContentPage.
Объединение пространства имен (рендеринг ServerContentPage) работает правильно, затем я сопоставляю все комнаты, извлеченные из базы данных MongoDB
, и создаю React Links, который перенаправляет меня на правильный маршрут и правильно отображает компонент.
{rooms.map(room => (
<StyledLink to={`/server/${currentNamespaceID}/room/${room._id}`} key={room._id}>
<StyledParagraph>{room.name}</StyledParagraph>
</StyledLink>
))}
Когда я нажимаю Link
, он отображает ChatPage
, как и ожидалось, затем я хочу, чтобы эта страница отправила сокет на сервер с roomID, который будет моей комнатой, к которой я должен присоединиться.
import React, { useContext, useEffect } from 'react';
import { connect } from 'react-redux';
import NamespaceSocketContext from '../providers/namespaceSocketContext';
const ChatPage = ({ match, setCurrentRoom }) => {
const { namespaceSocket } = useContext(NamespaceSocketContext);
useEffect(() => {
namespaceSocket.emit('join_room', match.params.roomID);
console.log('should emit join');
namespaceSocket.on('user_joined', roomID => {
console.log(`user joined to the room room ${roomID}`);
setCurrentRoom(roomID);
});
namespaceSocket.on('user_left', roomID => {
console.log(`user left room ${roomID}`);
setCurrentRoom(null);
});
return () => {
console.log('should emit leave');
namespaceSocket.emit('leave_room', match.params.roomID);
};
}, [match.params.roomID]);
return (
<p>...some content</p>
);
};
const mapDispatchToProps = dispatch => {
return {
setCurrentRoom: roomID => dispatch(setCurrentRoom(roomID))
};
};
export default connect(null, mapDispatchToProps)(ChatPage);
Из-за вложенных маршрутов я использую контекст для предоставления сокета, который подключен к текущему пространству имен.
namespaceController
.getAllNamespaces()
.then(namespaces =>
namespaces.map(item => {
io.of(`/${item._id}`)
.use((namespaceSocket, next) => {
socketAuthentication(namespaceSocket, next);
})
.on('connection', namespaceSocket => {
const currentNamespace = io.of(`/${item._id}`);
/* join room */
namespaceSocket.on('join_room', roomID => {
namespaceSocket.join(roomID);
console.log(`user joined room ${roomID}`);
/* emit event back, user joined to the room */
namespaceSocket.emit('user_joined', roomID);
});
/* leave room */
namespaceSocket.on('leave_room', roomID => {
namespaceSocket.leave(roomID);
namespaceSocket.emit('user_left', roomID);
console.log(`User left room ${roomID}`);
});
});
})
)
.catch(error => console.log(error));
Когда я открою этот ChatPage через React Router Link, я получу вывод на консоль браузера:
should emit join
, что может выглядеть правильно, но сервер не входит в комнату. На моей консоли сервера я никогда не получу user joined room {some.id}
. Кроме того, когда я захочу изменить комнату, щелкнув другую ссылку, я получу вывод на консоль браузера:
should emit leave
should emit join
user left room {some.id}
Итак, снова похоже, что функция очистки работает правильно, из-за чего сервер покидает room, он отправляет событие user_left
обратно клиенту, но не присоединяется к комнате, даже вместо этого журнала консоли.
- Я попытался использовать немного другой подход и добавить к функции Link
onClick
, которая может выдавать событие join_room
при нажатии на ссылку, затем удалить namespaceSocket.emit('join_room', match.params.roomID)
из ChatPage
useEffect .
Это работает, но это немного странно, потому что событие join_room
отправляется на сервер при первом клике по ссылке, клиент присоединяется к комнате, но я нет ответа сервера на стороне клиента. Только после второго щелчка я получил user joined room {some.id}
назад.
Возможно, это проблема useEffect match.params, но я не могу найти решение. Может быть, кто-нибудь сможет мне помочь.