每次发射都会触发io.on连接

时间:2019-09-11 01:16:16

标签: javascript node.js reactjs socket.io

我在socket.io上拥有的每个事件处理程序都首先调用io.on连接。 例如-我进行了一次聊天,每次我发送一条消息(将其发送给所有客户端)时,都会调用io.on连接,然后才移至事件处理程序。

服务器文件

const express = require('express');
const app = express();
const http = require('http').createServer(app);
const io = require('socket.io')(http);


app.get('/', function (req, res) {
    res.sendFile(__dirname + '/index.html');
});
let clients = 0;
io.on('connection', function (socket) {
    clients++;
    console.log('a user connected');
    io.sockets.emit('User Connected', clients + ' clients connected');
    socket.on('disconnect', function () {
        clients--;
        console.log('user disconnected');
        io.sockets.emit('connected', clients + ' clients connected');
    });

    socket.on('msg sent', (msg) => {
        console.log(msg, "new msg emit");
        io.sockets.emit('msg sent', msg);
    });
    socket.on('user typing', (msg) => {
        io.sockets.emit('user typing', msg);
    });
});




http.listen(process.env.PORT || 8080, () => {
    console.log('server started')
});

客户端聊天文件


import React, {Component} from 'react';

import socketIOClient from 'socket.io-client';
import Button from "react-bootstrap/Button";


class Chat extends Component {
    constructor(props) {
        super(props);
        this.state = {endpoint: "localhost:8080", messages: [], newMsg: '', typing: '', connected: ''}
    }

    send = () => {
        const socket = socketIOClient(this.state.endpoint);
        socket.emit('msg sent', this.state.newMsg);
        this.setState({typing: ''});
    };

    sentMsg = (event) => {
        const socket = socketIOClient(this.state.endpoint);
        socket.emit('user typing', 'User Typing');
        this.setState({newMsg: event.target.value});
        console.log(this.state.newMsg);
    };


    componentDidMount() {
        const socket = socketIOClient(this.state.endpoint);
        socket.on('msg sent', (msg) => {
            console.log(msg, 'msg!!!');
            this.setState({messages: [...this.state.messages, msg], typing: ''});

        });
        socket.on('user typing', (msg) => {
            this.setState({typing: msg})
        });
        socket.on('User Connected', (msg) => {
            this.setState({connected: msg});
        })
    };

    renderMsg = () => {
        if (this.state.messages.length > 0) {
            return this.state.messages.map((msg) => {
                return (<div className="border border-primary rounded m-2 p-1">{msg}</div>)
            })
        }
    };

    render() {
        return (
            <div className="container-fluid">
                <div className="row chat p-2">
                    <div className="col-2 border-primary border m-1">
                        {this.state.connected}
                    </div>
                    <div className="col-9 border-danger border m-1 ">
                        {this.renderMsg()}
                        <br/>
                        {this.state.typing}
                    </div>
                </div>
                <div className="row">
                    <div className="fixed-bottom text-center">
                        <input type="text" className="col-6" onChange={this.sentMsg}/>
                        <Button className="m-1" onClick={() => {
                            this.send()
                        }}>Send</Button>
                    </div>

                </div>

            </div>
        )
    }

};

export default Chat;

希望只有在建立新连接时,连接的客户端计数器才会更新并添加1,但是它会在我发送给它的每个事件中都这样做。

2 个答案:

答案 0 :(得分:1)

每次添加一条消息时,您的react组件都会重新呈现,这将触发componentDidMount并创建新的套接字。

考虑使用以下方法优化组件:

  1. 使用shouldComponentUpdate()https://reactjs.org/docs/react-component.html#shouldcomponentupdate
  2. 设置PureComponent https://reactjs.org/docs/react-api.html#reactpurecomponent
  3. 使用[]作为第二个参数https://reactjs.org/docs/hooks-reference.html#timing-of-effects,将功能组件和设置代码从componentDidMount切换到useEffect Hook。确保只运行一次。

答案 1 :(得分:1)

最近我们遇到了同样的问题,每次客户端更改状态时,都会创建与ws服务器的新连接,从而导致多个websocket连接。 我们所做的是@Chris Chen的答案中的第三名

我们使用Web-worker进行socket.io ws连接,但是您只能以简单的反应来实现它。这个想法毕竟是一样的。

function WrapContainer(props) {
  const { provider, pairString } = props.state;
  useEffect(() => {
    const worker = new Worker('../../workers/blox_worker.js', {
      type: 'module',
    });
    worker.postMessage({
      marketPair: props.state.pairString,
      provider: props.state.provider,
    });

    // Handling of websockets data here which basically event listener from worker
    //...
    // ..
    return () => {
      worker.terminate();
    };
  }, []);
}

您可以用socket.disconnect()替换worker.terminate; 代码中的return部分意为componentWillUnmount。 这将确保在销毁组件时,websocket也将被杀死。如果您不杀死websocket,则使用websocket导航回该组件时,它也会创建另一个连接。 因此,如果要与每个用户建立一致的1个websocket连接,添加此功能至关重要。

编辑:

在进一步研究代码时,似乎您每次发送消息时都在重新创建socket.io客户端。客户端应该只是一个实例,因为每次您调用socketIOClient新的ws连接时,也会创建一个新的ws连接,并且也应该对该实例进行发送。对于您的方案,更有效的用例是具有一个变量,该变量将保存socketio客户端。

import React, {Component} from 'react';

import socketIOClient from 'socket.io-client';
import Button from "react-bootstrap/Button";


class Chat extends Component {
    constructor(props) {
        super(props);
        this.state = {endpoint: "localhost:8080", messages: [], newMsg: '', typing: '', connected: ''}
    }

    send = () => {
        this.socket.emit('msg sent', this.state.newMsg);
        this.setState({typing: ''});
    }

    sentMsg = (event) => {
        this.socket.emit('user typing', 'User Typing');
        this.setState({newMsg: event.target.value});
        console.log(this.state.newMsg);
    }


    componentDidMount() {
        this.socket = socketIOClient(this.state.endpoint);
        this.socket.on('msg sent', (msg) => {
            console.log(msg, 'msg!!!');
            this.setState({messages: [...this.state.messages, msg], typing: ''});

        });
        this.socket.on('user typing', (msg) => {
            this.setState({typing: msg})
        });
        this.socket.on('User Connected', (msg) => {
            this.setState({connected: msg});
        })
    }

    renderMsg = () => {
        if (this.state.messages.length > 0) {
            return this.state.messages.map((msg) => {
                return (<div className="border border-primary rounded m-2 p-1">{msg}</div>);
            })
        }
    };

    render() {
        return (
            <div className="container-fluid">
                <div className="row chat p-2">
                    <div className="col-2 border-primary border m-1">
                        {this.state.connected}
                    </div>
                    <div className="col-9 border-danger border m-1 ">
                        {this.renderMsg()}
                        <br/>
                        {this.state.typing}
                    </div>
                </div>
                <div className="row">
                    <div className="fixed-bottom text-center">
                        <input type="text" className="col-6" onChange={this.sentMsg}/>
                        <Button className="m-1" onClick={() => {
                            this.send()
                        }}>Send</Button>
                    </div>

                </div>

            </div>
        )
    }

};