componentDidMount被多次调用

时间:2019-02-22 00:05:23

标签: reactjs socket.io react-redux

我正在尝试为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
    }
}

0 个答案:

没有答案