Я пытаюсь внедрить библиотеку socket.io
в приложение React для практики.Я впервые использую redux
для приложений любого типа.
Опишите проблему
Я создал компонент Chatroom.js
, где в componentDidMount
я отправляю действие для подключения к сокету и прослушивания событий.
componentDidMount() {
console.log('---Chatroom did mount')
console.log('isLoaded: ' + this.props.socket.isLoaded)
// if I remove this if-statement the compoenent re-renders
// and a new socket is created
if (!this.props.socket.isLoaded) {
this.props.userLoginToSocket()
.then(() => this.props.receive())
.then(() => this.props.setLoaded(true))
.then(() => this.props.sendUsername(this.props.auth.user.username))
}
}
Я реализовал промежуточное промежуточное программное обеспечение для обработки соединения socket.io, как было предложено в этом сообщении .
. Когда я запускаю приложение, я получаю этот журнал
Chatroom.js:55 ---Chatroom did mount
Chatroom.js:58 isLoaded: false
Chatroom.js:76 ---Chatroom will unmount
Messages.js:38 Messages component didMount
Chatroom.js:55 ---Chatroom did mount
Chatroom.js:58 isLoaded: true
ИcomponentWillReceiveProps
никогда не исполняется.Я не думаю, что это ожидаемое поведение, и componentDidMount
следует вызывать только один раз.Кроме того, я не могу понять, почему componentWillUnmount
срабатывает.
Когда я получаю сообщение от сервера, журнал выглядит как
Chatroom.js:76 ---Chatroom will unmount
Messages.js:38 Messages component didMount
Chatroom.js:55 ---Chatroom did mount
Chatroom.js:58 isLoaded: true
, который ясно указывает, что каждый раз, когда я отправляю действие, компонентдемонтирует и перемонтирует.
Полный код
Полный код проекта можно найти по адресу github .
// ./Chatroom.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { userLoginToSocket, receive, sendUsername, disconnect, setLoaded, emit } from '../actions/socketAction';
import Messages from './Messages'
class Chatroom extends Component {
constructor(props) {
super(props)
this.handleSend = this.handleSend.bind(this)
}
componentDidMount() {
console.log('---Chatroom did mount')
// console.log('socket.isConnected: ' + this.props.socket.isConnected)
// console.log('socket.isConnecting: ' + this.props.socket.isConnecting)
console.log('isLoaded: ' + this.props.socket.isLoaded)
// if I remove this if-statement the compoenent re-renders
// and a new socket is created
if (!this.props.socket.isLoaded) {
this.props.userLoginToSocket()
.then(() => this.props.receive())
.then(() => this.props.setLoaded(true))
.then(() => this.props.sendUsername(this.props.auth.user.username))
}
}
componentWillUnmount() {
// every time a new message is recieved the component willUnmount
// i want on component will unmount to disconnect from the socket
console.log('---Chatroom will unmount')
}
componentWillReceiveProps(nextProps) {
console.log('---Component will receive props')
}
handleSend() {
this.props.emit('Hello')
}
render() {
return (
<div>
<h1>Chatroom</h1>
{ this.props.socket.isLoaded &&
<Messages messages={this.props.messages.messages}/>
}
<button onClick={this.handleSend}>Send</button>
</div>
);
}
}
Chatroom.propTypes = {
socket: PropTypes.object,
messages: PropTypes.object
}
const mapStateToProps = (state) => {
return({
socket: state.socket,
messages: state.messages
})
}
export default withRouter(
connect(mapStateToProps, { userLoginToSocket , receive, sendUsername, disconnect, setLoaded, emit })(Chatroom)
)
// ./Messages.js
import React, { Component } from 'react'
class Messages extends Component {
componentDidMount() {
console.log('Messages component didMount')
}
render() {
return (
<div>
<h3>Messages</h3>
<ul>
{this.props.messages.map((item, ind) => <li key={ind}>{item.message}</li>)}
</ul>
</div>
)
}
}
export default Messages
Действия
// ../actions/socketAction
export const setLoaded = (boolean) => {
return {
type : "SET_LOADED",
boolean
}
}
export const userLoginToSocket = () => {
return (dispatch) => {
return dispatch({
type: 'socket',
types: ["CONNECT", "CONNECT_SUCCESS", "CONNECT_FAIL"],
promise: (socket) => socket.connect()
});
}
}
export function disconnect() {
return {
type: 'socket',
types: ["DISCONNECT", "DISCONNECT_SUCCESS", "DISCONNECT_FAIL"],
promise: socket => socket.disconnect(),
}
}
export const receive = () => {
return (dispatch) => {
const newMessage = (message) => {
return dispatch({
type: "NEW_MESSAGE_FROM_SOCKET",
payload: message,
});
};
return dispatch({
type: 'socket',
types: ["RECEIVE_", "RECEIVE_SUCC", "RECEIVE_FAIL"],
promise: (socket) => socket.on('ReceiveMessage', newMessage),
});
}
}
export const sendUsername = (user) => {
return (dispatch) => {
return dispatch({
type: 'socket',
types: ["SEND_USER", "SEND_USER_SUCCESS", "SEND_USER_FAIL"],
promise: (socket) => socket.emit('SET_USERNAME', user),
});
}
}
export const emit = (message) => {
return (dispatch) => {
return dispatch({
type: 'socket',
types: ["SEND", "SEND_SUCCESS", "SEND_FAIL"],
promise: (socket) => socket.emit('SEND_MESSAGE', message),
});
}
}
Редукторы
// ../socketReducer.js
const initialState = {
isConnected: false,
isConnecting: false,
isLoaded: false,
messageRecieved: false
}
export default function socketReducer(state=initialState, action) {
switch (action.type) {
case "CONNECTED":
return {
...state,
isConnected: true
}
case "DISCONNECTED":
return {
...state,
isConnected: false
}
case "NEW_MESSAGE_FROM_SOCKET":
return {
...state,
messageRecieved: true
}
case "SET_LOADED":
return {
...state,
isLoaded: action.boolean
}
default:
return state
}
}
// ../messagesReducer.js
const initialState = {
messages: [{message: "initial"}],
isRecieving: false,
didRecieve: false
}
export default function(state = initialState, action) {
switch (action.type) {
case "NEW_MESSAGE_FROM_SOCKET":
return {
...state,
messages: [...state.messages, action.payload]
}
default :
return state
}
}