我正在使用 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.js
至 handleSubmit
在 Chat.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
, andshouldComponentUpdate
methods- Class component static
getDerivedStateFromProps
method- Function component bodies
- State updater functions (the first argument to
setState
)- Functions passed to
useState
,useMemo
, oruseReducer
注意最后一行:传递给
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/