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

Hei*_*ann 6 express socket.io reactjs

我正在使用 express.js、react.js 和 socket.io 构建一个具有聊天功能的应用程序。这是在 Github 上重现的错误的最小版本

问题是服务器socket.on()总是触发两次,这对我来说毫无意义。

据我所知,客户端只发送一个.emit,但服务器.on总是触发两次(我相当确定事件处理程序没有被绑定两次),无论连接了多少客户端,或者实际上我是如何'我们重构了代码。

发送客户端的表单(输入 + 按钮)将消息附加到其本地消息列表中,然后分派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()
Run Code Online (Sandbox Code Playgroud)

这是来自的提交处理程序代码Chat.js(这似乎工作正常,只分派一次):

const handleSubmit = e => {
  e.preventDefault()

  if (message === '') return

  setMessages(messages => [...messages, message])
  dispatch({ type: 'MESSAGE_NEW', payload: message })
  setMessage('')
}
Run Code Online (Sandbox Code Playgroud)

这是/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
  }
}
Run Code Online (Sandbox Code Playgroud)

但这是来自 的相关部分/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)
  })
})
Run Code Online (Sandbox Code Playgroud)

更新:我发现将socket.emitfrom移动Reducer.jshandleSubmitinChat.js使其工作正常。我不明白为什么因为 Reducer 不会触发两次。

Luc*_*emy 5

根据严格模式下的 React 文档

\n
\n

严格模式可以\xe2\x80\x99t自动为您检测副作用,但它可以通过使副作用更具确定性来帮助您发现它们。这是通过有意两次调用以下函数来完成的:

\n
    \n
  • 类组件 constructor rendershouldComponentUpdate方法
  • \n
  • 类组件静态getDerivedStateFromProps方法
  • \n
  • 功能组件体
  • \n
  • 状态更新器函数( 的第一个参数setState
  • \n
  • 传递给useStateuseMemo、 或 的函数useReducer
  • \n
\n
\n

注意最后一行:传递给的函数useReducer将被调用两次;这就是为什么您的消息被发送两次的原因。您可以通过更新您的减速器来确认这一点,如下所示:

\n
  case "MESSAGE_NEW":\n      alert("I\'m running");\n
Run Code Online (Sandbox Code Playgroud)\n

您会注意到此警报出现两次,这要归功于React.StrictMode

\n

如果去掉<React.StrictMode>包装,src/App.js消息test中将不再重复;然而,它并没有解决这里的根本问题。

\n

根本问题是你让你的减速器(应该是纯粹的)执行不纯粹的副作用(在这种情况下,通过 websocket 发送消息)。此外,reducer 函数还有一个useContext调用 \xe2\x80\x93 ,从 React 的角度来看,这也是一个空操作,不是一个好的实践。

\n

正如您在问题中遇到的那样,只需将 移入socket.emit您的handleSubmit即可。或者,如果您希望采用减速器路线,则可以考虑更复杂的设置;一些流行的配置包括 Redux + Redux Thunk 或 Redux + Redux Saga。你的旅费可能会改变。

\n