我正在尝试为React应用程序实现socket.io
库以进行练习。这是我第一次为应用程序实现任何形式的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))
}
}
我实现了post中提出的redux中间件来处理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
}
}