reactjs - 服务器的 socket.io 监听器无缘无故地触发两次

标签 reactjs express socket.io

我正在使用 express.js、react.js 和 socket.io 构建一个具有聊天功能的应用程序。 Here is a minimal version with the bug reproduced on Github .
问题是服务器的socket.on()总是开火两次,这对我来说毫无意义。
据我所知,客户端只发送一个 .emit , 但服务器的 .on不管连接了多少客户端,或者实际上我是如何重构代码的,总是会触发两次(我相当确定事件处理程序不会被绑定(bind)两次)。
发送客户端的表单(输入 + 按钮)将消息附加到其本地消息列表,然后分派(dispatch) MESSAGE_NEW到 reducer 。
这是第一个 Socket.js文件。

import React from 'react'
import socketio from 'socket.io-client'

export const socket = socketio.connect('ws://localhost:3001')
export const SocketContext = React.createContext()
这是来自 Chat.js 的提交处理程序代码(这似乎工作正常,只发送一次):
const handleSubmit = e => {
  e.preventDefault()

  if (message === '') return

  setMessages(messages => [...messages, message])
  dispatch({ type: 'MESSAGE_NEW', payload: message })
  setMessage('')
}
这是/Reducer.js , 其中 .emit是信号(似乎也工作正常,只发射一次)。
import { useContext } from 'react'
import { SocketContext } from './Socket'

export default function Reducer(state, action) {
  const socket = useContext(SocketContext)

  switch (action.type) {
    case 'MESSAGE_NEW':
      // this only fires once, as it should
      console.log('emitting socket')
      socket.emit('message:new', action.payload)
      break

    default:
      return state
  }
}
但这是来自 /server/index.js 的相关信息,这就是出错的地方。无论我尝试过什么,.on总是开火两次。
io.on('connection', socket => {
  console.log('New client connected:', socket.id)

  socket.on('message:new', message => {
    // this always fires twice, i do not understand why
    console.log('message:new: ', message, socket.id)
    socket.broadcast.emit('message:new', message)
  })
})
更新:我发现移动socket.emit来自 Reducer.jshandleSubmitChat.js使它工作得很好。我不明白为什么因为 Reducer 不会触发两次。

最佳答案

根据 React documentation's on strict mode :

Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:

  • Class component constructor, render, and shouldComponentUpdate methods
  • Class component static getDerivedStateFromProps method
  • Function component bodies
  • State updater functions (the first argument to setState)
  • Functions passed to useState, useMemo, or useReducer

注意最后一行:传递给 useReducer 的函数将被调用两次;这就是为什么您的消息被发送两次的原因。您可以通过像这样更新 reducer 来确认这一点:
  case "MESSAGE_NEW":
      alert("I'm running");
感谢 React.StrictMode,您会注意到此警报出现了两次.
如果删除 <React.StrictMode>包装在 src/App.js test消息将不再重复;然而,它并没有解决这里的潜在问题。
潜在的问题是你让你的 reducer (应该是纯的)执行不纯的副作用(在这种情况下,通过 websocket 发送消息)。另外,reducer 函数还有一个 useContext调用——从 React 的角度来看,这也是一个空操作,不是好的做法。
正如您在问题中遇到的那样,只需移动 socket.emit进入您的 handleSubmit你应该很高兴去。或者,如果您想走 reducer 路线,您可以考虑更复杂的设置;一些流行的配置包括 Redux + Redux Thunk 或 Redux + Redux Saga。你的旅费可能会改变。

关于reactjs - 服务器的 socket.io 监听器无缘无故地触发两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68987508/

相关文章:

node.js - 在 KrakenJS/Express 中加载 socket.io 作为模块

javascript - 模块构建失败 : ReferenceError: window is not defined

javascript - 如何阻止第二个 React 计时器从零循环回来?

angularjs - 使用 express.js,我可以从根目录外部访问静态文件吗?

node.js - 想要弄清楚NodeJS应用程序结构(完整的JavaScript堆栈)

node.js - 在 node.js 和 Mongoose 中添加 friend 路线的正确方法?

Python heroku 为socket.io 聊天应用程序配置procfile Gunicorn + gevent |运行时错误: You need to use the gevent-websocket server

javascript - 增加 create-react-app 项目中的 JavaScript 堆大小

javascript - 数组或迭代器中的每个子项都应该有一个唯一的 key prop - 但我确实定义了一个 key prop

javascript - 无法获得 sequelize findbyPk 以向我的 React 应用程序返回单个值